<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Qt 6.5.3 - Implementing Google One Tap Sign On for Android]]></title><description><![CDATA[<p dir="auto">I've been trying to implement Google's one tap sign on for android. I've been trying to follow the steps listed in their docs <a href="https://developers.google.com/identity/one-tap/android/get-saved-credentials#java" target="_blank" rel="noopener noreferrer nofollow ugc">https://developers.google.com/identity/one-tap/android/get-saved-credentials#java</a>, but I don't really know anything about java so I've been flying by the seat of my pants.</p>
<p dir="auto">I have my java file <code>GoogleAuth.java</code> (shown below), and I try to call <code>DisplayOneTapSignIn</code> in my cpp code:</p>
<pre><code class="language-cpp">// GoogleAuth.cpp
if (QJniObject::isClassAvailable("com/test/google/GoogleAuth")) {
        qDebug() &lt;&lt; "Found GoogleAuth Class";
        QJniObject googleAuth = QJniObject("com/test/google/GoogleAuth");
        googleAuth.callObjectMethod&lt;void&gt;("DisplayOneTapSignIn");
    } else {
        qDebug() &lt;&lt; "Could not find GoogleAuth Class";
    }
</code></pre>
<p dir="auto">This doesn't work, though and fails with:</p>
<p dir="auto"><code>Can't create handler inside thread that has not called Looper.prepare()</code></p>
<p dir="auto">I don't really know how to proceed from here. From what I've read I need to run my java class in a UI thread. I tried a few things in the java class that I found on threads, but none of them work.</p>
<p dir="auto">This lead me to find this from Qt 5.15 <a href="https://doc.qt.io/qt-5/qtandroidextras-customactivity-example.html" target="_blank" rel="noopener noreferrer nofollow ugc">https://doc.qt.io/qt-5/qtandroidextras-customactivity-example.html</a></p>
<p dir="auto">Do I need to proceed by doing something along the lines in the Qt 5.15 example link above since my java class <code>extends AppCompatActivity</code>? Or should I be doing something via java like creating a wrapper class that runs my <code>GoogleAuth</code> class in a ui thread? If the latter, any suggestions on how to do that?</p>
<pre><code class="language-java">// GoogleAuth.java
package com.test.google;

import android.util.Log;
import com.google.android.gms.auth.api.identity.SignInClient;
import com.google.android.gms.auth.api.identity.BeginSignInRequest;
import com.google.android.gms.auth.api.identity.BeginSignInRequest.Builder;
import com.google.android.gms.auth.api.identity.BeginSignInRequest.GoogleIdTokenRequestOptions;
import com.google.android.gms.auth.api.identity.BeginSignInResult;
import com.google.android.gms.auth.api.identity.Identity;
import android.os.PersistableBundle;
import android.os.Bundle;
import android.os.Looper;
import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.OnFailureListener;

public class GoogleAuth extends AppCompatActivity {
    // ...

    private static final String TAG = "GoogleAuth";

    private static final int REQ_ONE_TAP = 2;
    private boolean showOneTapUI = true;
  
    private SignInClient oneTapClient;
    private BeginSignInRequest signInRequest;
  
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState,
                         @Nullable PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);

        Log.d(TAG, "onCreate start...");
  
        // This is where your UI code goes.
        oneTapClient = Identity.getSignInClient(this);
        signInRequest = BeginSignInRequest.builder()
        // .setPasswordRequestOptions(PasswordRequestOptions.builder()
        //         .setSupported(true)
        //         .build())
        .setGoogleIdTokenRequestOptions(GoogleIdTokenRequestOptions.builder()
                .setSupported(true)
                // Your server's client ID, not your Android client ID.
                .setServerClientId("my-client-id")
                // Only show accounts previously used to sign in.
                .setFilterByAuthorizedAccounts(true)
                .build())
        // Automatically sign in when exactly one credential is retrieved.
        .setAutoSelectEnabled(true)
        .build();

        Log.d(TAG, "onCreate end...");

    }

    public void DisplayOneTapSignIn() {
        Log.d(TAG, "DisplayOneTapSignIn start...");

        oneTapClient.beginSignIn(signInRequest)
                .addOnSuccessListener(this, new OnSuccessListener&lt;BeginSignInResult&gt;() {
                @Override
                public void onSuccess(BeginSignInResult result) {
                        try {
                        startIntentSenderForResult(
                                result.getPendingIntent().getIntentSender(), REQ_ONE_TAP,
                                null, 0, 0, 0);
                        } catch (IntentSender.SendIntentException e) {
                        Log.e(TAG, "Couldn't start One Tap UI: " + e.getLocalizedMessage());
                        }
                }
                })
                .addOnFailureListener(this, new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                        // No saved credentials found. Launch the One Tap sign-up flow, or
                        // do nothing and continue presenting the signed-out UI.
                        Log.d(TAG, e.getLocalizedMessage());
                }
                });
        Log.d(TAG, "DisplayOneTapSignIn end...");
    }
    // ...
  }

</code></pre>
]]></description><link>https://forum.qt.io/topic/151974/qt-6-5-3-implementing-google-one-tap-sign-on-for-android</link><generator>RSS for Node</generator><lastBuildDate>Sat, 15 Aug 2026 15:50:10 GMT</lastBuildDate><atom:link href="https://forum.qt.io/topic/151974.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 08 Nov 2023 22:52:11 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Qt 6.5.3 - Implementing Google One Tap Sign On for Android on Sun, 26 Nov 2023 15:31:52 GMT]]></title><description><![CDATA[<p dir="auto">A little progress:</p>
<p dir="auto">Running <code>setup</code> below works without error, but then when running <code>showOneTapSignOn</code> I get the following error:</p>
<pre><code>E AndroidRuntime: FATAL EXCEPTION: main
E AndroidRuntime: Process: com.auth.google, PID: 16379
E AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'android.app.ActivityThread$ApplicationThread android.app.ActivityThread.getApplicationThread()' on a null object reference
E AndroidRuntime: 	at android.app.Activity.startIntentSenderForResultInner(Activity.java:5970)
E AndroidRuntime: 	at android.app.Activity.startIntentSenderForResult(Activity.java:5941)
E AndroidRuntime: 	at androidx.activity.ComponentActivity.startIntentSenderForResult(ComponentActivity.java:772)
E AndroidRuntime: 	at androidx.core.app.ActivityCompat$Api16Impl.startIntentSenderForResult(ActivityCompat.java:816)
E AndroidRuntime: 	at androidx.core.app.ActivityCompat.startIntentSenderForResult(ActivityCompat.java:283)
E AndroidRuntime: 	at androidx.activity.ComponentActivity$2.onLaunch(ComponentActivity.java:228)
E AndroidRuntime: 	at androidx.activity.result.ActivityResultRegistry$2.launch(ActivityResultRegistry.java:175)
E AndroidRuntime: 	at androidx.activity.result.ActivityResultLauncher.launch(ActivityResultLauncher.java:47)
E AndroidRuntime: 	at com.auth.google.GoogleAuth$3.onSuccess(GoogleAuth.java:196)
E AndroidRuntime: 	at com.auth.google.GoogleAuth$3.onSuccess(GoogleAuth.java:191)
E AndroidRuntime: 	at com.google.android.gms.tasks.zzm.run(com.google.android.gms:play-services-tasks@@18.0.2:1)
E AndroidRuntime: 	at android.os.Handler.handleCallback(Handler.java:958)
E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:99)
E AndroidRuntime: 	at android.os.Looper.loopOnce(Looper.java:205)
E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:294)
E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:8177)
E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
</code></pre>
<p dir="auto">C++ side:</p>
<pre><code class="language-cpp">GoogleAuth::GoogleAuth(QObject *parent)
    : QObject{parent}
{
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([=]() {
        if (QJniObject::isClassAvailable("com/auth/google/GoogleAuth")) {
            qDebug() &lt;&lt; "Found GoogleAuth Class";
            m_gauth = QJniObject("com/auth/google/GoogleAuth");
            if (!m_gauth.isValid()) {
                qDebug() &lt;&lt; "m_gauth object not valid";
            } else {
                qDebug() &lt;&lt; "m_gauth Valid";
                m_gauth.callMethod&lt;void&gt;("setup", QNativeInterface::QAndroidApplication::context());
            }
        } else {
            qDebug() &lt;&lt; "Could not find GoogleAuth Class";
        }
    }).waitForFinished();
}

...

void GoogleAuth::Auth()
{
    QNativeInterface::QAndroidApplication::runOnAndroidMainThread([=]() {
        m_gauth.callMethod&lt;void&gt;("showOneTapSignOn", QNativeInterface::QAndroidApplication::context());
    }).waitForFinished();
}
</code></pre>
<p dir="auto">Java side:</p>
<p dir="auto">Note that I've altered the function below based on this video which says the Google docs uses a deprecated function: <a href="https://www.youtube.com/watch?v=Zz3412C4BSA" target="_blank" rel="noopener noreferrer nofollow ugc">https://www.youtube.com/watch?v=Zz3412C4BSA</a></p>
<pre><code class="language-java">public class GoogleAuth extends AppCompatActivity {

    private SignInClient oneTapClient;
    private BeginSignInRequest signUpRequest;
    private ActivityResultLauncher&lt;IntentSenderRequest&gt; activityResultLauncher;
    private static final String TAG = "GoogleAuth";
    private static final int REQ_ONE_TAP = 2;

    public GoogleAuth() {
        Log.i(TAG, "GoogleAuth ctor running");
    }

    public void setup(Context qtContext) {
        if (qtContext == null) {
            Log.i(TAG, "qtContext == null");
        }
        oneTapClient = Identity.getSignInClient(qtContext);
        Log.i(TAG, "oneTapClient created");
        signUpRequest = BeginSignInRequest.builder()
                .setGoogleIdTokenRequestOptions(GoogleIdTokenRequestOptions.builder()
                        .setSupported(true)
                        // Your server's client ID, not your Android client ID.
                        .setServerClientId("&lt;server client id&gt;")
                        .setFilterByAuthorizedAccounts(false)
                        .build())
                .build();

        activityResultLauncher =
            registerForActivityResult(new ActivityResultContracts.StartIntentSenderForResult(),
            new ActivityResultCallback&lt;ActivityResult&gt;() {
                @Override
                public void onActivityResult(ActivityResult result) {
                    Log.i(TAG, "onActivityResult start");
                    if (result.getResultCode() == Activity.RESULT_OK) {
                        Log.i(TAG, "result.getResultCode() == Activity.RESULT_OK");
                        try {
                            SignInCredential credential = oneTapClient.getSignInCredentialFromIntent(result.getData());
                            String idToken = credential.getGoogleIdToken();
                            if (idToken != null) {
                                Log.i(TAG, "Got ID token");
                                // authenticate with backend server here!
                                String email = credential.getId();
                                Log.i(TAG, "Email = " + email);
                            }
                        } catch (ApiException e) {
                            Log.i(TAG, "ApiException!!");
                            e.printStackTrace();
                        }
                    }
                }
            });
    }

    public void showOneTapSignOn(Context qtContext) {
        Log.i(TAG, "showOneTapSignOnSTART!");
        oneTapClient.beginSignIn(signUpRequest)
            .addOnSuccessListener((Activity)qtContext, new OnSuccessListener&lt;BeginSignInResult&gt;() {
                @Override
                public void onSuccess(BeginSignInResult result) {
                    IntentSenderRequest intentSenderRequest =
                            new IntentSenderRequest.Builder(result.getPendingIntent().getIntentSender()).build();
                    activityResultLauncher.launch(intentSenderRequest);
                }
            })
            .addOnFailureListener((Activity)qtContext, new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    // No Google Accounts found. Just continue presenting the signed-out UI.
                    Log.d(TAG, e.getLocalizedMessage());
                }
            });
        Log.i(TAG, "showOneTapSignOn END!");
    }
  }
</code></pre>
]]></description><link>https://forum.qt.io/post/780864</link><guid isPermaLink="true">https://forum.qt.io/post/780864</guid><dc:creator><![CDATA[gween]]></dc:creator><pubDate>Sun, 26 Nov 2023 15:31:52 GMT</pubDate></item></channel></rss>