Android中GoogleAuthUtil.getTokenWithNotification意图回调未触发。

17

我有一个后台服务,调用 GoogleAuthUtl.getTokenWithNotification,它可以正常工作,但我正在尝试实现此函数的回调部分,但这部分并不正常工作。

我已经实现了广播接收器,并将其添加到了清单中,我的应用程序中也有一个活动。以下是相关的代码片段。

GoogleAuthUtil.getTokenWithNotification

GoogleAuthUtil.getTokenWithNotification(this.getContext(), account, "oauth2:" + GmailScopes.GMAIL_SEND, null, new Intent(AuthReceiver.AUTH_INTENT));

AuthReceiver
public class AuthReceiver extends BroadcastReceiver
{
    public final static String AUTH_INTENT = "com.testoauth.AUTH_INTENT";

    public AuthReceiver()
    {
    }

    @Override
    public void onReceive(Context context, Intent intent)
    {
        Log.d("RECEIVER", "Received Auth broadcast.");
        NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancelAll();
    }
}

AndroidManifest
<receiver android:name=".AuthReceiver" android:enabled="true" android:exported="true">
    <intent-filter>
        <action android:name="com.testoauth.AUTH_INTENT" />
    </intent-filter>
</receiver>

我完全不知道为什么它没有接收广播。在日志中我没有看到任何异常,也没有任何迹象表明接收器被调用过,即使在调试时也不会停在断点上。我做错了什么吗?
编辑
我使用的是min sdk 16和target sdk 25。
GoogleAuthUtil.getTokenWithNotification API文档中:
此方法专门为后台任务提供。如果出现需要用户干预的错误,此方法会处理相关通知。用户解决通知后,回调将被广播。如果用户取消,则不会触发回调。
无论用户是否取消,回调都不会触发。除了 ActivityManager 显示通知已经被显示 (Displayed com.google.android.gms/.auth.uiflows.gettoken.GetTokenActivity) 外,在日志中没有任何指示特定的广播意图 (在这个例子中是 com.testoauth.AUTH_INTENT) 已经被发送。"Received Auth broadcast." 的消息也没有出现在日志中。
包含此功能的 SDK 示例 (<android-sdk>/extras/google/google_play_services/samples/auth/gau) 甚至无法正常工作。

你找到那个问题的解决方案了吗?我也尝试了同样的方法,但对我也不起作用。 - Emanuel
很抱歉,我没有。 - vane
只是确认一下,AuthReceiver.AUTH_INTENT = "com.testoauth.AUTH_INTENT";,对吧?***///*** 可能无关:不要使用隐式意图,每个新平台版本(现在是Android O)都会带来限制[(https://developer.android.com/preview/features/background.html#broadcasts)],而且您的情况根本不需要隐式意图。只需创建 new Intent(context, com.package.AuthReceiver.class) 即可。 - Eugen Pechanec
@EugenPechanec 这不是问题,我已经测试过了,观察到相同的错误行为。 - vane
4个回答

1

我尝试在Android API 25上跟踪错误,但回调函数从未被调用:

  • 没有网络
  • 用户尚未登录
  • 用户尚未授权代表他/她发送电子邮件的权限
  • Google Play服务已禁用
  • Google Play服务已过时

如果回调方法对您的用例不是至关重要的话,您可以按照Gmail API Android快速入门的步骤代表用户发送电子邮件。请参阅发送电子邮件以创建消息。 您还可以检查使用上述教程创建的MyGoogleAuthUtilApplication

希望这可以帮助你。


是的,我在查找“MimeMessage”库时遇到了问题。最终,“https://mvnrepository.com/artifact/com.sun.mail/android-mail” <- 这个库对我有用。您可以查看我的GitHub项目。 - BhalchandraSW
我测试过了,这是一个非常好的发送电子邮件的解决方案(我将要迁移到它),但它仍然没有解决回调问题。如果我使用它,我仍然必须要么有一个损坏的回调,要么创建自己的通知而不使用内置的Android通知。 - vane
你提供的示例MyGoogleAuthUtilApplication使用了getTokenWithNotification和回调函数,所以我将进行测试,但我几乎可以确定,即使在该示例中电子邮件的发送是有效的,回调函数实际上并没有被调用。 - vane
我已经尝试过了,它的效果很好,但仍然无法解决当用户按下授权屏幕上的“允许”按钮时回调不触发的问题。此外,由于我使用的是API级别16+,因此无法使用EasyPermissions库回调。我可以在后台服务中使用它来发送电子邮件,但如果电子邮件发送失败,我仍需要创建自己的通知。 - vane
我已经在做这个了,这正是我的后端服务目前的工作方式。如果我检测到权限方面的电子邮件发送失败,我会为用户创建一个通知,让他们稍后点击并选择允许或拒绝。GoogleAuthUtil 应该为您处理这个并允许回调,但回调已经失效了,所以我必须实现自己的通知。因为它在服务中,所以当我尝试发送电子邮件时无法检测到 Allow,直到很久以后用户才会选择 Allow - vane
显示剩余3条评论

1

从GoogleAuthUtil和Plus.API迁移

如果您过去使用GoogleAuthUtil.getToken或Plus.API集成了Google登录,则应迁移到最新的登录API,以获得更高的安全性和更好的用户体验。
参考: https://developers.google.com/identity/sign-in/android/migration-guide

同时请查看此内容,看它是否有帮助。

http://www.programcreek.com/java-api-examples/index.php?source_dir=AndroidAppDeployer-master/AndroidAppDeployer/src/com/appjma/appdeployer/service/DownloadService.java

http://www.programcreek.com/java-api-examples/index.php?source_dir=AndroidAppDeployer-master/AndroidAppDeployer/src/com/appjma/appdeployer/receiver/AuthReceiver.java


我之前尝试过这两个例子,它们都不起作用。我开始认为这是Google Play服务的实际错误。至于Plus.API,据我所知,它是让用户登录到Google并授予应用程序访问其帐户某些方面的方式,而我正在尝试创建的应用程序仅需要并且只想要访问发送电子邮件和使用GoogleAuthUtil允许我们在用户的Google帐户中显示连接的应用程序以及它需要访问的内容。 - vane

1
我已经实施了 demo,我使用了最新版本的认证 gradle,并且它正在工作。看起来可能是认证版本存在问题。
public class AuthActivity extends Activity {


    private static final int AUTHORIZATION_CODE = 1993;
    private static final int ACCOUNT_CODE = 1601;

    private AuthPreferences authPreferences;
    private AccountManager accountManager;

    /**
     * change this depending on the scope needed for the things you do in
     * doCoolAuthenticatedStuff()
     */
    private final String SCOPE = "https://www.googleapis.com/auth/googletalk";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        accountManager = AccountManager.get(this);

        authPreferences = new AuthPreferences(this);
        if (authPreferences.getUser() != null
                && authPreferences.getToken() != null) {
            doCoolAuthenticatedStuff();
        } else {
            chooseAccount();
        }
    }

    private void doCoolAuthenticatedStuff() {
        // TODO: insert cool stuff with authPreferences.getToken()

        Log.e("AuthApp", authPreferences.getToken());
        clickSendEmail();
    }

    private void chooseAccount() {
        // use https://github.com/frakbot/Android-AccountChooser for
        // compatibility with older devices
        Intent intent = AccountManager.newChooseAccountIntent(null, null,
                new String[] { "com.google" }, false, null, null, null, null);
        startActivityForResult(intent, ACCOUNT_CODE);
    }

    private void requestToken() {
        Account userAccount = null;
        String user = authPreferences.getUser();
        for (Account account : accountManager.getAccountsByType("com.google")) {
            if (account.name.equals(user)) {
                userAccount = account;
Preferences.setAccount(AuthActivity.this,account.name, account.type);
                break;
            }
        }

        accountManager.getAuthToken(userAccount, "oauth2:" + SCOPE, null, this,
                new OnTokenAcquired(), null);
    }

    /**
     * call this method if your token expired, or you want to request a new
     * token for whatever reason. call requestToken() again afterwards in order
     * to get a new token.
     */
    private void invalidateToken() {
        AccountManager accountManager = AccountManager.get(this);
        accountManager.invalidateAuthToken("com.google",
                authPreferences.getToken());

        authPreferences.setToken(null);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_OK) {
            if (requestCode == AUTHORIZATION_CODE) {
                requestToken();
            } else if (requestCode == ACCOUNT_CODE) {
                String accountName = data
                        .getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
                authPreferences.setUser(accountName);

                // invalidate old tokens which might be cached. we want a fresh
                // one, which is guaranteed to work
                invalidateToken();

                requestToken();
            }
        }
    }

    private class OnTokenAcquired implements AccountManagerCallback<Bundle> {

        @Override
        public void run(AccountManagerFuture<Bundle> result) {
            try {
                Bundle bundle = result.getResult();

                Intent launch = (Intent) bundle.get(AccountManager.KEY_INTENT);
                if (launch != null) {
                    startActivityForResult(launch, AUTHORIZATION_CODE);
                } else {
                    String token = bundle
                            .getString(AccountManager.KEY_AUTHTOKEN);

                    authPreferences.setToken(token);

                    doCoolAuthenticatedStuff();
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    private void clickSendEmail()
    {
        final Account account = Preferences.getAccount(this);
        final String token = Preferences.getToken(this);

        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {

                    Session session = Session.getDefaultInstance(new Properties(), null);

                    MimeMessage email = new MimeMessage(session);

                    email.setFrom(new InternetAddress(account.name));

                    //TODO: change email address to your email address for testing
                    email.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress("nasitraj2@gmail.com"));
                    email.setSubject("TEST OAUTH EMAIL");
                    email.setText("This is a test");
                    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
                    email.writeTo(bytes);
                    String encodedEmail = Base64.encodeBase64URLSafeString(bytes.toByteArray());
                    Message message = new Message();
                    message.setRaw(encodedEmail);

                    Intent intent = new Intent(AUTH_INTENT);
                    PendingIntent pendingIntent = PendingIntent.getBroadcast(AuthActivity.this, 0, intent, 0);
                    AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
                    alarmManager.set(AlarmManager.RTC_WAKEUP,  System.currentTimeMillis()
                            , pendingIntent);
                    String test = GoogleAuthUtil.getTokenWithNotification(AuthActivity.this, account, "oauth2:" + GmailScopes.GMAIL_SEND, null, intent);
                    GoogleCredential credential = new GoogleCredential();
                    credential.setAccessToken(test);

                    boolean changed = false;
                    if (!test.equals(token))
                    {
                        changed = true;
                        //   Snackbar.make(AuthActivity.this.getView(), "TOKEN CHANGED", Snackbar.LENGTH_LONG).show();
                        Preferences.setToken(AuthActivity.this, test);
                    }



                    Gmail service = new Gmail.Builder(AndroidHttp.newCompatibleTransport(),
                            AndroidJsonFactory.getDefaultInstance(), credential)
                            .setApplicationName("Test OAuth").build();

                    service.users().messages().send("me", message).execute();

                    String msg = "Email sent";
                    if (changed)
                        msg = "TOKEN CHANGED: " + msg;



                }
                catch (MessagingException e)
                {
                    Log.d( "ERROR", e.getMessage());
                }
                catch (GoogleJsonResponseException e)
                {
                    if (e.getDetails().getCode() == 401)
                    {
                        try
                        {
                            Intent intent = new Intent(AUTH_INTENT);
                            GoogleAuthUtil.clearToken(AuthActivity.this, Preferences.getToken(AuthActivity.this));
                            GoogleAuthUtil.getTokenWithNotification(AuthActivity.this, account, "oauth2:" + GmailScopes.GMAIL_SEND, null, intent);
                        }
                        catch (Exception e1)
                        {
                            //ignore
                        }
                    }
                }
                catch (IOException e)
                {
                    //  Snackbar.make(AuthActivity.this.getView(), "ERROR", Snackbar.LENGTH_LONG).show();
                    Log.d( "ERROR", e.getMessage());
                }
                catch (Exception e)
                {
                    //Snackbar.make(AuthActivity.this.getView(), "ERROR", Snackbar.LENGTH_LONG).show();
                    Log.d( "ERROR", e.getMessage());
                }
            }
        }).start();
    }
}

这并不能解决我的问题。AccountManager.getAuthToken 仅适用于前台操作。我需要使用 GoogleAuthUtil.getTokenWithNotification,因为它在后台的服务中执行,并且还会在状态栏中放置通知,而 getAuthToken 不会。 - vane
哦,现在这里已经太晚了。明天我会查看服务。但我有一个问题,您必须仅使用AccountManager一次来获取令牌,然后就不需要了。那么如果您从UI获取令牌,对您来说是否不好?另外,在没有UI的情况下,用户如何选择帐户? - Rajesh N
不,我需要在后台服务启动时检查令牌。这是为了能够在稍后调用服务之前向用户显示通知,如果他们在其Google帐户设置中取消了应用程序的授权。否则,由于令牌不再有效,使用令牌将失败。 - vane
你的意思是如果用户未经授权,则将在后台服务中从通知中进行授权过程?你能发布你的服务代码吗? - Rajesh N
不好意思,我不能发布代码,但我希望我能。基本上它的工作原理是这样的:1)服务启动并尝试获取令牌2)调用getTokenWithNotification失败3)通过Google Play服务getTokenWithNotification在状态栏中放置通知4)稍后用户单击通知5)显示允许/拒绝对话框(来自Google Play服务)6)不起作用;当用户单击允许时,回调将触发,因此我可以进行一些清理工作。第6步不起作用,并存在错误。 - vane

0

似乎没有人能够给出这个问题的正确答案;有很多绝对好的建议来解决这个问题,但是没有回答实际问题的东西。我得出结论,这一定是 Android 或 Google Play 服务中的一个 bug。不幸的是,我已经向 Android 问题跟踪器和 Google Play 服务支持论坛报告了此问题...两者都互相指责,拒绝甚至查看此问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接