在无谷歌服务的安卓手机上实现谷歌登录
事情的起因
因为公司的app是在google store上架的,然后我们的国产机售卖的时候没有预装谷歌服务。于是用户就因为无法使用谷歌登录我司的app一直在被刷差评。我的评价是狗屁理由,折磨我😣。
领导一看🤔,这样不行🙅♂️。我们必须在没有谷歌服务的机器上能够支持谷歌登录。说了一句肯定有办法能够实现这个功能的,让我多看看。
这个项目是用flutter写的app,但感觉这个思路感觉是安卓都可以用的。下面会贴出flutter和h5的代码。
过程
我先是问了一问大佬,并得到回答谷歌插件必须在有谷歌登录的机子上才能使用。我记得在没有谷歌服务的手机上调用是会报一个12500的错误,具体的我忘了。
使用webview的方法实现
h5实现谷歌登录
关于使用h5实现谷歌登录,网上都能查到,不多bb,直接贴代码。
其中个人认为比较值得注意的地方有plugin_name,我开了Google People API。{prompt:'select_account'}添加这个参数可以在点击登录的时候选择账号。否则之前已经登过了,会直接登录。
能够在网页上能够成功登录,并且在控制台看到输出信息,应该就没有问题了。
我测试的地址:yuwenhao.site/google/
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<div style="width: 100px;height: 100px;background-color: red" onclick="openwindow()"></div>
<button id="customBtn" type="button">Google登录</button>
<div id="name"></div>
<button type="button" onclick="signOut();">Sign out</button>
<script src="https://accounts.google.com/gsi/client" async defer></script>
<script src="https://apis.google.com/js/api:client.js" async defer></script>
<script src="https://apis.google.com/js/platform.js?onload=onLoadCallback" async defer></script>
<script>
var googleUser = {};
var auth2
window.onLoadCallback = function () {
gapi.load('auth2', function () {
// Retrieve the singleton for the GoogleAuth library and set up the client.
auth2 = gapi.auth2.init({
client_id: '*******.apps.googleusercontent.com', //客户端ID
cookiepolicy: 'single_host_origin',
scope: 'profile', //可以请求除了默认的'profile' and 'email'之外的数据
plugin_name: 'Google People API'
});
attachSignin(document.getElementById('customBtn'));
});
};
function attachSignin(element) {
auth2.attachClickHandler(element, {prompt:'select_account'},
function (googleUser) {
document.getElementById('name').innerText = "Signed in: " + googleUser.getBasicProfile().getName();
var profile = auth2.currentUser.get().getBasicProfile();
var authResponse = auth2.currentUser.get().getAuthResponse();
console.log(authResponse.getAccessToken)
console.log('======')
console.log(auth2.currentUser.get())
console.log(auth2.currentUser.get('access_token'))
console.log('ID: ' + profile.getId());
console.log('Full Name: ' + profile.getName());
console.log('Given Name: ' + profile.getGivenName());
console.log('Family Name: ' + profile.getFamilyName());
console.log('Image URL: ' + profile.getImageUrl());
console.log('Email: ' + profile.getEmail());
window.flutter_inappwebview.callHandler('handlerFoo', profile,authResponse).then(function (result) {
console.log(profile.getId());
});
// window.location.href='https://www.baidu.com/';
}, function (error) {
console.log(JSON.stringify(error, **undefined**, 2));
});
}
function openwindow() {
window.open('https://www.baidu.com/')
}
//注销
function signOut() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
alert('用户注销成功');
});
}
</script>
</body>
</html>
flutter端 完成谷歌登录
在完成了h5的google登录之后,使用webview打开谷歌登录会出现403的问题。
这里需要修改一下webview的useragent。
'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36',
这样就可以解决403的问题。
接下来才是问题的重点。
因为当你点击google登录的按钮时,在网页上查看你是可以明确的看到,他会弹出一个新的tab或者弹出一个窗口来让你进行登录,在完成登录之后,之前点击登录页面的登录成功代码才会继续执行下去。
当我使用弹出窗口的模式进行谷歌登录时,我发现他在完成谷歌登录流程后一直处于下面这种状态。这应该就是因为之前的登录页面已经没了,而他还在苦苦的给登录页面发送消息。(好家伙这是什么虐恋💔)
尝试使用重定向时,发现登录成功后的代码没有执行到(废话,网页都没了,什么给你执行😒)。
最后换了个思路才最终成功。这里非常感谢大佬的翻译的这篇文章,救我于水火之中。
使用webview插件: flutter_inappwebview
这里面提到的这个章节(如何管理用target="_blank "或 "window.open "打开的弹出窗口。),对我帮助很大。
里面就提到了安卓的webview能够支持多个弹框,只需将 supportMultipleWindows,javaScriptCanOpenWindowsAutomatically 设置为true。再在onCreateWindow中写入处理的方法,就能打开一个新的网页。
当用户点击
target="_blank "
的链接或通过使用window.open
的JavaScript代码来管理弹出窗口时,你可以使用onCreateWindow
事件。在Android上,为了能够允许这个事件,你需要将supportMultipleWindows
选项设置为true。另外,为了能够允许使用JavaScript,你需要将javaScriptCanOpenWindowsAutomatically
设置为true。如果你想管理这些请求,你应该从这个事件中返回
true
,否则,这个事件的默认实现什么也不做,因此返回false
。
CreateWindowRequest
代表导航请求,它包含一个windowId
,可以用来创建,例如,一个新的InAppWebView
实例。这个windowId
被本地代码用来映射该请求和用于管理该请求的WebView。 另外,CreateWindowRequest
包含了请求的url
(在Android上,如果弹出窗口是用window.open
的JavaScript打开的,它将是null
),但是如果你需要维护Window
JavaScript对象引用(用window.open
方法创建的),例如,调用window.close
方法,那么你应该用windowId
创建新的WebView,而不使用url。 下面是一个简单的例子,当用户点击链接时,会显示一个AlertDialog
。
最后贴一下dart的完整代码
import 'package:coupert/Util/logger_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_svg/flutter_svg.dart';
// import 'package:flutter_screenutil/size_extension.dart';
class GoogleLoginWebView extends StatefulWidget {
final ValueChanged<Map> onchange;
const GoogleLoginWebView({Key key, this.onchange}) : super(key: key);
@override
State<GoogleLoginWebView> createState() => _GoogleLoginWebViewState();
}
class _GoogleLoginWebViewState extends State<GoogleLoginWebView> {
InAppWebViewController _webViewController;
InAppWebViewController _webViewPopupController;
BuildContext popupContext;
InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
userAgent:
'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36',
javaScriptEnabled: true,
useShouldOverrideUrlLoading: true,
useOnLoadResource: true,
cacheEnabled: false,
javaScriptCanOpenWindowsAutomatically: true,
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true,
supportMultipleWindows: true,
));
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
height: 675.h - 48,
child: Column(
children: [
Container(
height: 50,
padding: EdgeInsets.only(left: 17, right: 17),
decoration: BoxDecoration(
color: Color(0xFFEA5A4F),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
Navigator.of(context).pop();
},
child: SvgPicture.asset(
"images/Account/account_login_close.svg",
width: 18.5,
height: 18.5,
),
),
Text(
'Google sign in',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white),
),
Container(
height: 18.5,
width: 18.5,
)
],
),
),
Expanded(child: InAppWebView(
initialUrlRequest:
URLRequest(url: Uri.parse('https://yuwenhao.site/google/')),
initialOptions: options,
onWebViewCreated: (InAppWebViewController controller) {
_webViewController = controller;
_webViewController.addJavaScriptHandler(
handlerName: 'handlerFoo',
callback: (args) {
print('webViewController.addJavaScriptHandler=======');
logger.d('webViewController.addJavaScriptHandler=======');
logger.d(args);
if(popupContext!=null){
Navigator.pop(popupContext);
popupContext=null;
}
Navigator.pop(context,args);
});
},
onCreateWindow: (controller, createWindowRequest) async {
print("onCreateWindow");
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
isScrollControlled: true,
builder: (context) {
popupContext = context;
return Container(
height: 675.h - 48,
child: InAppWebView(
windowId: createWindowRequest.windowId,
initialOptions: options,
onWebViewCreated:
(InAppWebViewController controller) {
_webViewPopupController = controller;
},
onLoadStart: (controller, url) {
print("onLoadStart popup $url");
},
onLoadStop: (controller, url) {
print("onLoadStop popup $url");
},
),
);
});
// showDialog(
// context: context,
// builder: (context) {
// return AlertDialog(
// content: Container(
// width: MediaQuery.of(context).size.width,
// height: 400,
// child: InAppWebView(
// // Setting the windowId property is important here!
// windowId: createWindowRequest.windowId,
// initialOptions: options,
// onWebViewCreated:
// (InAppWebViewController controller) {
// _webViewPopupController = controller;
// },
// onLoadStart: (controller, url) {
// print("onLoadStart popup $url");
// },
// onLoadStop: (controller, url) {
// print("onLoadStop popup $url");
// },
// ),
// ),
// );
// },
// );
return true;
},
))
],
),
),
),
);
}
}
历程&感受:
说实话这个玩意儿,第一次跟我提是在5月份的时候,那时候我还被疫情关在家里面,吃一顿饿一顿的日子。查了一天,没有查到在无谷歌服务的安卓手机上怎么实现谷歌登录,最后在下午尝试了一下使用webview解决。卡在了403这个地方。
第二次省略。。。主要是不想做,在列举了其他app都不能完成谷歌登录,推掉了😁。
第三次是在上个星期跟我又提了一下,我开始查方法,这次倒是解决了出现403的这个问题。卡在了没有办法回调这里,然后就先做其他的需求了。
第四次就是在这个星期,其他需求都没了。任务很明确就解决谷歌登录。。。。mmp,泪崩😭。不过挺幸运的,两天后就解决了。
解决这个问题,前前后后大概花了我4天时间😮💨,掉了几十根头发(我感觉应该快100了)。以此文章纪念我逝去的时间和掉落的头发。
感受就是我对“人都是逼出来的”这句话有了更深的感受😮💨。。。
最后小弟,第一次写博客,有什么没讲清楚的可以再评论区讲一下。时间跨度有点长,我有可能没记全我还踩了哪些坑。
虽然不一定会看,但应该会看😂😂😂😂😂
图侵必删
还有一件事,转载请注明出处。