草庐IT

这么递名片才高效!教你给应用开发个交换名片功能

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

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

开发具体步骤如下:

1. 开发准备

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

2. 添加权限

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

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

3. 代码开发

3.1 动态权限申请

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

@Override public void onStart() { super.onStart(); getActivity().getApplication().registerActivityLifecycleCallbacks(this); checkPermission(); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { for (int i = 0; i < permissions.length; ++i) { if (grantResults[i] != 0) { showWarnDialog(Constants.LOCATION_ERROR); } } } private void checkPermission() { if (!BluetoothCheckUtil.isBlueEnabled()) { showWarnDialog(Constants.BLUETOOTH_ERROR); return; } if (!LocationCheckUtil.isLocationEnabled(this.getActivity())) { showWarnDialog(Constants.LOCATION_SWITCH_ERROR); return; } if (!NetCheckUtil.isNetworkAvailable(this.getActivity())) { showWarnDialog(Constants.NETWORK_ERROR); return; } String[] deniedPermission = PermissionUtil.getDeniedPermissions(this.getActivity(), new String[] { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }); if (deniedPermission.length > 0) { PermissionUtil.requestPermissions(this.getActivity(), deniedPermission, 10); } } 3.2 封装名片发布接口和名片订阅接口

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

private MessageHandler mMessageHandler = new MessageHandler() { @Override public void onFound(Message message) { CardInfo cardInfo = JsonUtils.json2Object(new String(message.getContent(), Charset.forName("UTF-8")), CardInfo.class); if (cardInfo == null) { return; } mSearchCardDialogFragment.addCardInfo(cardInfo); } @Override public void onLost(Message message) { CardInfo cardInfo = JsonUtils.json2Object(new String(message.getContent(), Charset.forName("UTF-8")), CardInfo.class); if (cardInfo == null) { return; } mSearchCardDialogFragment.removeCardInfo(cardInfo); } }; private void publish(String namespace, String type, int ttlSeconds, OnCompleteListener<Void> listener) { Message message = new Message(JsonUtils.object2Json(mCardInfo).getBytes(Charset.forName("UTF-8")), type, namespace); Policy policy = new Policy.Builder().setTtlSeconds(ttlSeconds).build(); PutOption option = new PutOption.Builder().setPolicy(policy).build(); Nearby.getMessageEngine(getActivity()).put(message, option).addOnCompleteListener(listener); } private void subscribe(String namespace, String type, int ttlSeconds, OnCompleteListener<Void> listener, GetCallback callback) { Policy policy = new Policy.Builder().setTtlSeconds(ttlSeconds).build(); MessagePicker picker = new MessagePicker.Builder().includeNamespaceType(namespace, type).build(); GetOption.Builder builder = new GetOption.Builder().setPolicy(policy).setPicker(picker); if (callback != null) { builder.setCallback(callback); } Nearby.getMessageEngine(getActivity()).get(mMessageHandler, builder.build()).addOnCompleteListener(listener); } 3.3  名片交换菜单处理

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

private boolean onExchangeItemSelected() { PinCodeDialogFragment dialogFragment = new PinCodeDialogFragment(passwrod -> { MyCardFragment.this.publish(passwrod, passwrod, Policy.POLICY_TTL_SECONDS_MAX, result -> { if (!result.isSuccessful()) { String str = "Exchange card fail, because publish my card fail. exception: " + result.getException().getMessage(); Log.e(TAG, str); Toast.makeText(getActivity(), str, Toast.LENGTH_LONG).show(); return; } MyCardFragment.this.subscribe(passwrod, passwrod, Policy.POLICY_TTL_SECONDS_INFINITE, ret -> { if (!ret.isSuccessful()) { MyCardFragment.this.unpublish(passwrod, passwrod, task -> { String str = "Exchange card fail, because subscribe is fail, exception(" + ret.getException().getMessage() + ")"; if (!task.isSuccessful()) { str = str + " and unpublish fail, exception(" + task.getException().getMessage() + ")"; } Log.e(TAG, str); Toast.makeText(getActivity(), str, Toast.LENGTH_LONG).show(); }); return; } mSearchCardDialogFragment.setOnCloseListener(() -> { MyCardFragment.this.unpublish(passwrod, passwrod, task -> { if (!task.isSuccessful()) { Toast.makeText(getActivity(), "Unpublish my card fail, exception: " + task.getException().getMessage(), Toast.LENGTH_LONG).show(); } }); MyCardFragment.this.unsubscribe(task -> { if (!task.isSuccessful()) { Toast.makeText(getActivity(), "Unsubscribe fail, exception: " + task.getException().getMessage(), Toast.LENGTH_LONG).show(); } }); }); mSearchCardDialogFragment.show(getParentFragmentManager(), "Search Card"); }, null); }); }); dialogFragment.show(getParentFragmentManager(), "pin code"); return true; } 3.4  收藏名片处理

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

@Override public void onFavorite(CardInfo cardInfo, boolean isFavorite) { if (isFavorite) { mFavoriteMap.put(cardInfo.getId(), cardInfo); } else { mFavoriteMap.remove(cardInfo.getId()); } Set<String> set = new HashSet<>(mFavoriteMap.size()); for (CardInfo card : mFavoriteMap.values()) { set.add(JsonUtils.object2Json(card)); } SharedPreferences sharedPreferences = getContext().getSharedPreferences("data", Context.MODE_PRIVATE); sharedPreferences.edit().putStringSet(Constants.MY_FAVORITES_KEY, set).apply(); } 本次给大家演示的demo用到了华为HMS Nearby service的近距离设备间消息订阅功能。基于Nearby Message能力不仅仅可以用来做面对面交换名片,还可以帮助开发者实现很多有趣的功能,例如:

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

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

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

有关这么递名片才高效!教你给应用开发个交换名片功能的更多相关文章

  1. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  2. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  3. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  4. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  5. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  6. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  7. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  8. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  9. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  10. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

随机推荐