【HarmonyOS】一文教你如何在H5页面中使用电话、定位及导航

发布时间 2023-06-15 11:17:04作者: Mayism123
【关键字】

HarmonyOS、H5页面、拨打电话、获取系统定位、跳转高德地图导航

 

【1、写在前面】

上一篇中我们带领大家实现了一个在低码项目中跳转加载H5页面的功能,有兴趣的可以参考以下文章:

https://developer.huawei.com/consumer/cn/forum/topic/0208121373041025092?fid=0102683795438680754

今天我们继续在上一篇的基础上继续开发,这次我们要实现的功能是在H5页面中点击按钮实现:①拨打电话、②获取系统定位、③拉起第三方地图应用开启导航功能(本文以高德地图为例),本项目是基于API6的JS工程,项目中使用的是JS FA调用Java PA机制(Java中使用WebView组件加载H5页面),OK,下面一起来实战一下吧。

完整代码见文末。

 

【2、H5页面】

想要实现上面的效果,我们的核心技术点就是JS跟Java的数据交互,可以参考以下官方文档中的实现:

https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-java-component-webview-0000001092715158#section6833162911117

首先准备一个H5页面,页面内容很简单,就是3个按钮,每个按钮绑定一个点击事件,点击事件中是JS调用Java的代码,然后将该文件放在本地entry/src/main/resources/rawfile目录下,这里仅仅是简单写一个H5页面做为测试使用,实际项目开发中此部分内容可以不用看,以实际H5页面地址为准。内容如下:

cke_105314.png​​

关于加载本地H5页面可以参考文档中的写法,文档地址如下:

https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-java-component-webview-0000001092715158#section1280373621914

cke_119739.png

 

【3、拨打电话】

前面在H5页面中已经定义了JS调用Java端的方法名及参数,这里参考文档中的写法:

https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-java-component-webview-0000001092715158#section6833162911117

cke_134200.png

然后在Java侧实现addJsCallback(),在回调方法onCallback中实现具体的拨打电话的业务逻辑:

cke_151115.png

 

【4、获取定位】

想要获取系统的位置信息,首先需要获取定位权限,关于权限的获取可以参考这篇文章中的实现:

https://ost.51cto.com/posts/5165

我们这里获取的是LOCATION权限,需要在config.json文件的module中添加权限配置,

cke_162052.png

然后需要编写动态获取权限的代码:

cke_175245.png

在获取权限之后,就可以编写获取位置信息的代码了,关于如何获取位置信息可以参考这篇文章:

https://www.51cto.com/article/679565.html

cke_188415.png

location对象中就可以获取到经纬度等位置信息了。然后同样的需要实现onCallback()方法:

cke_201582.png

 

【5、跳转导航】

这里我们是通过(逆)地理编码转化结合Scheme跳转来实现拉起导航功能的,(逆)地理编码转化的参考文档如下:

https://developer.harmonyos.com/cn/docs/documentation/doc-guides/device-location-geocoding-0000001053187503

高德地图导航的Scheme协议要求如下:

https://lbs.amap.com/api/amap-mobile/guide/android/navigation

跳转的代码就很简单啦:

cke_214791.png

同样的需要实现onCallback()方法:

cke_227980.png

 

【6、实现效果】

最后来看一下实现的效果吧:

6b090fab1b119aafeff78d716d41fc54_376x815.gif%40900-0-90-f.gif

 

【7、完整代码】

test.html文件:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试页面</title>
<script>
function callToApp() {
if (window.JsCallbackToCall && window.JsCallbackToCall.call) {
var result = JsCallbackToCall.call("10086");
}
}
function locationToApp() {
if (window.JsCallbackToLocation && window.JsCallbackToLocation.call) {
var result = JsCallbackToLocation.call("");
}
}
function naviToApp() {
if (window.JsCallbackToNavi && window.JsCallbackToNavi.call) {
var result = JsCallbackToNavi.call("南京市玄武湖");
}
}
</script>
</head>

<body>
<div align="center">
<button type="button" id="btn_call" "callToApp()">电话</button>
<button type="button" id="btn_location" "locationToApp()">定位</button>
<button type="button" id="btn_navi" "naviToApp()">导航</button>
</div>
</body>

</html>

相关文本字段strings.json中:

{
"name": "permission_location",
"value": "定位原因"
}

index.js中跳转H5Ability.java页面代码:

gotoH5Ability() {
featureAbility.startAbility({
want:
{
bundleName: "com.jarchie.h5",
abilityName: "com.jarchie.h5.H5Ability"
},
});
}

config.json中的权限配置:

"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:permission_location",
"usedScene": {
"ability": [
"com.jarchie.h5.H5Ability"
],
"when": "always"
}
}
]

H5Ability.java:

import com.jarchie.h5.slice.H5AbilitySlice;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.window.dialog.ToastDialog;
import ohos.bundle.IBundleManager;

import static com.jarchie.h5.slice.LocationAbilitySlice.MY_PERMISSIONS_REQUEST_LOCATION;

public class H5Ability extends Ability {
private H5AbilitySlice h5AbilitySlice;

@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(H5AbilitySlice.class.getName());
}

@Override
public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsFromUserResult(requestCode, permissions, grantResults);
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_LOCATION: {
// 匹配requestPermissions的requestCode
if (grantResults.length > 0
&& grantResults[0] == IBundleManager.PERMISSION_GRANTED) {
// 权限被授予之后做相应业务逻辑的处理
h5AbilitySlice.requestLocation();
} else {
// 权限被拒绝
new ToastDialog(getContext()).setText("权限被拒绝").show();
}
return;
}
}
}

public H5AbilitySlice getH5AbilitySlice() {
return h5AbilitySlice;
}

public void setH5AbilitySlice(H5AbilitySlice h5AbilitySlice) {
this.h5AbilitySlice = h5AbilitySlice;
}
}

ability_h5.xml:

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
 xmlns:ohos="http://schemas.huawei.com/res/ohos"
 ohos:height="match_parent"
 ohos:width="match_parent"
 ohos:alignment="horizontal_center"
 ohos:orientation="vertical">

<Text
 ohos:id="$+id:back"
 ohos:height="50vp"
 ohos:width="match_parent"
 ohos:start_margin="10vp"
 ohos:end_margin="10vp"
 ohos:text="返回"
 ohos:text_size="18vp"/>

<ohos.agp.components.webengine.WebView
 ohos:id="$+id:webview"
 ohos:height="match_parent"
 ohos:width="match_parent"/>
</DirectionalLayout>

H5AbilitySlice.java:

import com.jarchie.h5.H5Ability;
import com.jarchie.h5.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.components.Text;
import ohos.agp.components.webengine.*;
import ohos.agp.utils.TextTool;
import ohos.agp.window.dialog.ToastDialog;
import ohos.bundle.IBundleManager;
import ohos.global.resource.Resource;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.location.*;
import ohos.utils.net.Uri;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.util.List;

public class H5AbilitySlice extends AbilitySlice {
private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, H5AbilitySlice.class.getName());
private Text backText;
private WebView webView;
// 定位
public static final int MY_PERMISSIONS_REQUEST_LOCATION = 0;
private Locator locator;
private RequestParam requestParam;
private MyLocatorCallback locatorCallback;

@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_h5);
H5Ability h5Ability = (H5Ability) getAbility();
h5Ability.setH5AbilitySlice(this);
initBackText();
initWebView();
callPhone();
location();
navi();
}

private void navi(){
final String jsName = "JsCallbackToNavi";
webView.addJsCallback(jsName, new JsCallback() {
@Override
public String onCallback(String msg) {
gotoGaode(msg);
return "jsResult";
}
});
}

// 获取定位
private void location(){
final String jsName = "JsCallbackToLocation";
webView.addJsCallback(jsName, new JsCallback() {
@Override
public String onCallback(String msg) {
requestPermission();
return "jsResult";
}
});
}

// 拨打电话
private void callPhone(){
final String jsName = "JsCallbackToCall";
webView.addJsCallback(jsName, new JsCallback() {
@Override
public String onCallback(String msg) {
// 增加自定义处理
Intent intent = new Intent();
intent.setAction("ohos.intent.action.dial");
intent.setUri(Uri.parse("tel:"+msg));
startAbility(intent,0);
return "jsResult";
}
});
}

private void gotoGaode(String destination){
try {
// (逆)地理编码转换
GeoConvert geoConvert = new GeoConvert();
List<GeoAddress> geoList = geoConvert.getAddressFromLocationName(destination, 1);
GeoAddress geoAddress = geoList.get(0);
if (geoAddress == null)
return;
Intent intent1 = new Intent();
Operation operation = new Intent.OperationBuilder()
.withAction("android.intent.action.VIEW")
.withUri(Uri.parse("androidamap://navi?sourceApplication=amap&lat="+geoAddress.getLatitude()+"&lon="+geoAddress.getLongitude()+"&dev=1&style=2"))
.withFlags(Intent.FLAG_NOT_OHOS_COMPONENT)
.build();
intent1.setOperation(operation);
startAbility(intent1);
}catch (Exception e){
e.printStackTrace();
}
}

private void requestPermission() {
if (verifySelfPermission("ohos.permission.LOCATION") != IBundleManager.PERMISSION_GRANTED) {
// 应用未被授予权限
if (canRequestPermission("ohos.permission.LOCATION")) {
// 是否可以申请弹框授权(首次申请或者用户未选择禁止且不再提示)
requestPermissionsFromUser(new String[]{"ohos.permission.LOCATION"}, MY_PERMISSIONS_REQUEST_LOCATION);
} else {
// 显示应用需要权限的理由,提示用户进入设置授权
new ToastDialog(getContext()).setText("请进入系统设置进行授权").show();
}
} else {
// 权限已被授予
requestLocation();
}
}

public void requestLocation() {
locator = new Locator(this);
requestParam = new RequestParam(RequestParam.SCENE_NAVIGATION);
locatorCallback = new MyLocatorCallback();
locator.requestOnce(requestParam, locatorCallback); // 请求一次
// locator.startLocating(requestParam, locatorCallback); // 多次请求 直接启动服务
}

public class MyLocatorCallback implements LocatorCallback {
@Override
public void onLocationReport(Location location) {
if (location != null) {
getUITaskDispatcher().asyncDispatch(() -> {
// (逆)地理编码转换
GeoConvert geoConvert = new GeoConvert();
try {
List<GeoAddress> list = geoConvert.getAddressFromLocation(location.getLatitude(), location.getLongitude(), 1);
GeoAddress geoAddress = list.get(0);
if (geoAddress == null)
return;
new ToastDialog(getContext())
.setText("当前位置:经度:" + location.getLongitude() + "\n纬度:" + location.getLatitude()
+ "\n国家:" + geoAddress.getCountryName())
.show();
// "位置:" + geoAddress.getPlaceName()
} catch (IOException e) {
e.printStackTrace();
}
});
}
}

@Override
public void onStatusChanged(int type) {
}

@Override
public void onErrorReport(int type) {
}
}

// 初始化WebView
private void initWebView() {
webView = (WebView) findComponentById(ResourceTable.Id_webview);
webView.getWebConfig().setJavaScriptPermit(true); // 如果网页需要使用JavaScript,增加此行
webView.getWebConfig().setWebStoragePermit(true);
webView.setWebAgent(new WebAgent() {
@Override
public ResourceResponse processResourceRequest(WebView webview, ResourceRequest request) {
final String authority = "com.jarchie.h5";
final String rawFile = "/rawfile/";
final String local = "/local/";
Uri requestUri = request.getRequestUrl();
if (authority.equals(requestUri.getDecodedAuthority())) {
String path = requestUri.getDecodedPath();
if (TextTool.isNullOrEmpty(path)) {
return super.processResourceRequest(webview, request);
}
if (path.startsWith(rawFile)) {
// 根据自定义规则访问资源文件
String rawFilePath = "entry/resources/rawfile/" + path.replace(rawFile, "");
String mimeType = URLConnection.guessContentTypeFromName(rawFilePath);
try {
Resource resource = getResourceManager().getRawFileEntry(rawFilePath).openRawFile();
ResourceResponse response = new ResourceResponse(mimeType, resource, null);
return response;
} catch (IOException e) {
HiLog.info(TAG, "open raw file failed");
}
}
if (path.startsWith(local)) {
// 根据自定义规则访问本地文件
String localFile = getContext().getFilesDir() + path.replace(local, "/");
HiLog.info(TAG, "open local file " + localFile);
File file = new File(localFile);
if (!file.exists()) {
HiLog.info(TAG, "file not exists");
return super.processResourceRequest(webview, request);
}
String mimeType = URLConnection.guessContentTypeFromName(localFile);
try {
InputStream inputStream = new FileInputStream(file);
ResourceResponse response = new ResourceResponse(mimeType, inputStream, null);
return response;
} catch (IOException e) {
HiLog.info(TAG, "open local file failed");
}
}
}
return super.processResourceRequest(webview, request);
}
});
webView.load("https://com.jarchie.h5/rawfile/test.html");
}

// 初始化返回文本
private void initBackText() {
backText = (Text) findComponentById(ResourceTable.Id_back);
backText.setClickedListener(component -> onBackPressed());
}

@Override
public void onActive() {
HiLog.info(TAG, "onActive:");
super.onActive();
}

@Override
public void onForeground(Intent intent) {
HiLog.info(TAG, "onForeground:");
super.onForeground(intent);
}

@Override
protected void onStop() {
HiLog.info(TAG, "onStop:");
super.onStop();
locator.stopLocating(locatorCallback);
}
}