在工作和生活中,遇见新的同事或者合作伙伴,交换名片是一个常见的用户需求,纸质名片常忘带、易丢失,是客户的一个痛点。因此,市场上出现了很多交换电子名片的APP和小程序。那么,如何给自己的APP开发一个名片交换功能呢?

我们可以接入华为近距离通信服务,通过近距离设备间消息订阅(Nearby Message),快速实现一对一或一对多名片交换。下图是功能演示:

开发具体步骤如下:

如果您已经是华为的开发者,可以省略此步骤。如果您以前没有集成华为移动服务的经验,那么需要先配置AppGallery Connect,开通近距离通信服务并集成HMS SDK。相关步骤请参考官方文档

在使用Nearby Message之前,需要添加网络权限、蓝牙权限、位置权限。在工程的AndroidManifest.xml文件中添加如下权限:

  1. <uses-permission android:name="android.permission.INTERNET " />
  2. <uses-permission android:name="android.permission.BLUETOOTH" />
  3. <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  4. <!-- The location permission is also required in Android 6.0 or later. -->
  5. <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  6. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

3.1 动态权限申请

检查蓝牙开关、位置开关是否打开、网络是否可用,并对位置权限进行动态权限申请

  1. @Override
  2. public void onStart() {
  3. super.onStart();
  4. getActivity().getApplication().registerActivityLifecycleCallbacks(this);
  5. checkPermission();
  6. }
  7. @Override
  8. public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
  9. for (int i = 0; i < permissions.length; ++i) {
  10. if (grantResults[i] != 0) {
  11. showWarnDialog(Constants.LOCATION_ERROR);
  12. }
  13. }
  14. }
  15. private void checkPermission() {
  16. if (!BluetoothCheckUtil.isBlueEnabled()) {
  17. showWarnDialog(Constants.BLUETOOTH_ERROR);
  18. return;
  19. }
  20. if (!LocationCheckUtil.isLocationEnabled(this.getActivity())) {
  21. showWarnDialog(Constants.LOCATION_SWITCH_ERROR);
  22. return;
  23. }
  24. if (!NetCheckUtil.isNetworkAvailable(this.getActivity())) {
  25. showWarnDialog(Constants.NETWORK_ERROR);
  26. return;
  27. }
  28. String[] deniedPermission = PermissionUtil.getDeniedPermissions(this.getActivity(), new String[] {
  29. Manifest.permission.ACCESS_COARSE_LOCATION,
  30. Manifest.permission.ACCESS_FINE_LOCATION
  31. });
  32. if (deniedPermission.length > 0) {
  33. PermissionUtil.requestPermissions(this.getActivity(), deniedPermission, 10);
  34. }
  35. }

3.2 封装名片发布接口和名片订阅接口

订阅到的名片消息(onFound),把名片添加到查找名片对话框中显示;名片消息丢失时(onLost),从查找名片对话框中删除该名片

  1. private MessageHandler mMessageHandler = new MessageHandler() {
  2. @Override
  3. public void onFound(Message message) {
  4. CardInfo cardInfo = JsonUtils.json2Object(new String(message.getContent(), Charset.forName("UTF-8")),
  5. CardInfo.class);
  6. if (cardInfo == null) {
  7. return;
  8. }
  9. mSearchCardDialogFragment.addCardInfo(cardInfo);
  10. }
  11. @Override
  12. public void onLost(Message message) {
  13. CardInfo cardInfo = JsonUtils.json2Object(new String(message.getContent(), Charset.forName("UTF-8")),
  14. CardInfo.class);
  15. if (cardInfo == null) {
  16. return;
  17. }
  18. mSearchCardDialogFragment.removeCardInfo(cardInfo);
  19. }
  20. };
  21. private void publish(String namespace, String type, int ttlSeconds, OnCompleteListener<Void> listener) {
  22. Message message = new Message(JsonUtils.object2Json(mCardInfo).getBytes(Charset.forName("UTF-8")), type,
  23. namespace);
  24. Policy policy = new Policy.Builder().setTtlSeconds(ttlSeconds).build();
  25. PutOption option = new PutOption.Builder().setPolicy(policy).build();
  26. Nearby.getMessageEngine(getActivity()).put(message, option).addOnCompleteListener(listener);
  27. }
  28. private void subscribe(String namespace, String type, int ttlSeconds, OnCompleteListener<Void> listener,
  29. GetCallback callback) {
  30. Policy policy = new Policy.Builder().setTtlSeconds(ttlSeconds).build();
  31. MessagePicker picker = new MessagePicker.Builder().includeNamespaceType(namespace, type).build();
  32. GetOption.Builder builder = new GetOption.Builder().setPolicy(policy).setPicker(picker);
  33. if (callback != null) {
  34. builder.setCallback(callback);
  35. }
  36. Nearby.getMessageEngine(getActivity()).get(mMessageHandler, builder.build()).addOnCompleteListener(listener);
  37. }

3.3  名片交换菜单处理

面对面交换名片交换码,发布个人名片成功后,订阅名片消息

  1. private boolean onExchangeItemSelected() {
  2. PinCodeDialogFragment dialogFragment = new PinCodeDialogFragment(passwrod -> {
  3. MyCardFragment.this.publish(passwrod, passwrod, Policy.POLICY_TTL_SECONDS_MAX, result -> {
  4. if (!result.isSuccessful()) {
  5. String str = "Exchange card fail, because publish my card fail. exception: "
  6. + result.getException().getMessage();
  7. Log.e(TAG, str);
  8. Toast.makeText(getActivity(), str, Toast.LENGTH_LONG).show();
  9. return;
  10. }
  11. MyCardFragment.this.subscribe(passwrod, passwrod, Policy.POLICY_TTL_SECONDS_INFINITE, ret -> {
  12. if (!ret.isSuccessful()) {
  13. MyCardFragment.this.unpublish(passwrod, passwrod, task -> {
  14. String str = "Exchange card fail, because subscribe is fail, exception("
  15. + ret.getException().getMessage() + ")";
  16. if (!task.isSuccessful()) {
  17. str = str + " and unpublish fail, exception(" + task.getException().getMessage()
  18. + ")";
  19. }
  20. Log.e(TAG, str);
  21. Toast.makeText(getActivity(), str, Toast.LENGTH_LONG).show();
  22. });
  23. return;
  24. }
  25. mSearchCardDialogFragment.setOnCloseListener(() -> {
  26. MyCardFragment.this.unpublish(passwrod, passwrod, task -> {
  27. if (!task.isSuccessful()) {
  28. Toast.makeText(getActivity(), "Unpublish my card fail, exception: "
  29. + task.getException().getMessage(), Toast.LENGTH_LONG).show();
  30. }
  31. });
  32. MyCardFragment.this.unsubscribe(task -> {
  33. if (!task.isSuccessful()) {
  34. Toast.makeText(getActivity(), "Unsubscribe fail, exception: "
  35. + task.getException().getMessage(), Toast.LENGTH_LONG).show();
  36. }
  37. });
  38. });
  39. mSearchCardDialogFragment.show(getParentFragmentManager(), "Search Card");
  40. }, null);
  41. });
  42. });
  43. dialogFragment.show(getParentFragmentManager(), "pin code");
  44. return true;
  45. }

3.4  收藏名片处理

收藏名片时把名片加入收藏列表,名片取消收藏时把名片从参数列表中删除,并把数据保存到本地存储中。

  1. @Override
  2. public void onFavorite(CardInfo cardInfo, boolean isFavorite) {
  3. if (isFavorite) {
  4. mFavoriteMap.put(cardInfo.getId(), cardInfo);
  5. } else {
  6. mFavoriteMap.remove(cardInfo.getId());
  7. }
  8. Set<String> set = new HashSet<>(mFavoriteMap.size());
  9. for (CardInfo card : mFavoriteMap.values()) {
  10. set.add(JsonUtils.object2Json(card));
  11. }
  12. SharedPreferences sharedPreferences = getContext().getSharedPreferences("data", Context.MODE_PRIVATE);
  13. sharedPreferences.edit().putStringSet(Constants.MY_FAVORITES_KEY, set).apply();
  14. }

本次给大家演示的demo用到了华为HMS Nearby service的近距离设备间消息订阅功能。基于Nearby Message能力不仅仅可以用来做面对面交换名片,还可以帮助开发者实现很多有趣的功能,例如:

  • 竞技类手游中的面对面组队功能
  • 棋牌类手游中的面对面约局功能
  • 近场AA收款功能
  • 音乐曲目共享功能

欲了解更多详情

访问华为近距离通信服务官网
获取华为近距离通信服务开发指导文档
华为近距离通信服务开源仓库地址:GitHubGitee
华为HMS Core官方论坛 
解决集成问题请到Stack Overflow

关注我们,第一时间了解HMS Core最新技术~

版权声明:本文为hmscore原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/hmscore/p/15128528.html