用手指一挥即可控制Android手机

这些天,我对乐趣的定义包括修改不同的技术,并了解如何相互发挥作用。 就像一个疯狂的指挥疯狂地挥舞着双臂让乐队一起表演一样,我像疯了一样打字,直到脑海中终于有了一个想法。

在本文中,我将向您展示如何创建一个Android呼叫控制器,该控制器可以使手指绕着手指使来电铃声静音。 我们将使用以下技术:

上述技术构成了难题的三个重要部分:

  • 节点服务器 –我们将运行一个节点服务器,它是Android设备和一个非常简单的网页之间的桥梁。 它将跟踪我们是否希望手机静音。
  • on {X} –接到来电时,我们将要求电话轮询服务器以查看是否有静音请求。 如果是这样,它将使自己静音并发送忙音。
  • 带有Leap Motion控制器的网页–打开网页并在Leap Motion上移动时,我们向服务器发送请求以使其静音。

什么是Leap Motion控制器?

Leap Motion控制器是一种有趣的输入设备,可以检测您的手和手指的运动。 这是一项革命性的技术,它只是在等待一些令人难以置信的开发人员来实践纯魔术。 我们将使用Leap Motion团队的LeapJS API来控制JavaScript应用程序。

在{X}上

我发现JavaScript的主要优势之一是您可以轻松地使用该语言来配对完全不相关的设备。 在此演示中,我们将Leap Motion与on {X}配对。 on {X}是一个带有JavaScript API的Android应用,可让您控制和/或响应Android设备上的事件。 如果您以前没有在{X}上使用过,我将在我的文章“使用JavaScript和on {X}控制网页”中介绍它的基础知识。 检查一下,以快速了解情况。

Node.js服务器

index.js文件包含我们所有的服务器端Node应用程序代码。 我们网页的所有代码都将位于公用文件夹中。 这是我们旨在的目录结构:

项目文件结构

在本演示的package.json文件(如下所示)中没有太多要声明的内容。 我们将使用它来声明服务器的依赖关系。 在这种情况下,我们服务器的主要依赖关系是Express。

当您将应用程序部署到诸如Heroku之类的托管服务时,可以很好地包含engines字段。 他们会指出您期望使用哪个版本的Node和npm。

{
  "name": "LeapCallController",
  "version": "0.0.1",
  "dependencies": {
    "express": "3.1.x"
  },
  "engines": {
    "node": "0.10.x",
    "npm": "1.2.x"
  }
}

要在Heroku上运行我们的应用,我们需要一个Procfile ,其内容如下所示。

web: node index.js

服务器代码

Node服务器是我们带有Leap Motion控制器的网页和我们的Android设备之间的桥梁。 服务器代码如下所示。

var http = require('http'),
    express = require('express'),
    app = express(),
    server = require('http').createServer(app),
    port = process.env.PORT || 5000,
    call = {};
    call.sound = true;

app.use(express.bodyParser());

app.get('/', function(request, response) {
  response.sendfile('public/index.html');
});

app.post('/shouldibesilent', function(request, response) {
  console.log('That phone wants to know if it should be silent...', request);
  response.json({callSound: call.sound});
});

app.post('/call', function(request, response) {
  console.log('Something is setting the call to ' + request.body.action);

  switch (request.body.action) {
    case 'mute':
      call.sound = false;
      break;
    case 'reset':
      call.sound = true;
      break;
  }

  response.json({success: true, actionReceived: request.body.action});
});

app.get(/^(.+)$/, function(req, res) {
  res.sendfile('public/' + req.params[0]);
});

server.listen(port, function() {
  console.log('Listening on ' + port);
});

我们创建一个名为call的对象来存储有关调用状态的信息。 call.sound是一个布尔值,它指示我们是否有关闭声音的请求(使电话静音)。 在我们的演示中,我们将仅使用call.sound ,但是我将其放置在一个对象中,以便将来扩展应用程序的功能将非常简单。

与Android交流

以下路线将用于告诉我们的Android设备call.sound的值是什么。 我使用了JSON响应,因为我发现它似乎对{X} Ajax请求最有效。 调试时,我发现使用console.log()在服务器上记录这些请求非常方便。

app.post('/shouldibesilent', function(request, response) {
  console.log('That phone wants to know if it should be silent...', request);
  response.json({callSound: call.sound});
});

与Leap Motion连接

/callPOST路由负责处理电话采取行动的请求。 我们将发送静音电话的请求,从而将call.sound设置为false 。 负责处理此问题的代码如下所示。

app.post('/call', function(request, response) {
  switch (request.body.action) {
    case 'mute':
      call.sound = false;
      break;
    case 'reset':
      call.sound = true;
      break;
  }

  response.json({success: true, actionReceived: request.body.action});
});

客户端JavaScript代码

对于当前访问的任何人,我们public目录中的index.html页通常都很乏味且无趣。 您可以展开它以显示有关来电的信息的仪表板,或在您进行不同手势时显示可视化效果,以向用户提供有关他们是否成功进行手势的反馈。 在本演示中,我们不会对此进行深入探讨。

在今天的演示中,我们将重点介绍提供Leap Motion输入的JavaScript。 我仅包括jQuery来使用Ajax功能,但是您可以在原始JavaScript中实现相同的效果。

<script src="jquery-1.7.2.min.js"></script>

我还在LeapJS中包含了Underscore,因为我发现LeapJS API的某些版本需要它:

<script src="js/underscore.min.js"></script>
<script src="js/leap.min.js"></script>

前端JavaScript代码如下所示。 为了简化本教程,已将JavaScript与HTML内联放置。

var controller = new Leap.Controller({enableGestures: true}),
    callMuteRequestMade = false;

controller.loop(function(frame) {
  var gesture  = frame.gestures[0],
      type = gesture ? gesture.type : '';

  if (type == 'circle') {
    console.log('Circle');

    if (!callMuteRequestMade) {
      // Only ask it to mute once!
      callMuteRequestMade = true;

      $.ajax({
        url: '/call',
        type: 'POST',
        data: {
          action: 'mute'
        }
      });
    }
  }
});

controller.on('ready', function() {
  console.log('ready');
});
controller.on('connect', function() {
  console.log('connect');
});
controller.on('disconnect', function() {
  console.log('disconnect');
});
controller.on('focus', function() {
  console.log('focus');
});
controller.on('blur', function() {
  console.log('blur');
});
controller.on('deviceConnected', function() {
  console.log('deviceConnected');
});
controller.on('deviceDisconnected', function() {
  console.log('deviceDisconnected');
});

以下行设置了Leap Motion控制器并启用了手势,以便我们可以检测到手指的盘旋。 您还可以使用手势来检测滑动和手指点击。 从现在开始,我们将使用controller变量与Leap Motion进行交互:

var controller = new Leap.Controller({enableGestures: true})

JavaScript末尾的各种controller.on()函数用于调试目的。 每个人都会让我们知道Leap Motion设备的状态何时发生变化。

我们主要对controller.loop()函数感兴趣,该函数在Leap Motion检测到的每个帧上重复运行。 根据API文档,这大约是每秒60次。 如果您做的任何事情都占用大量资源,请记住这一点,因为它将经常运行!

在我们的代码中, controller.loop()每个帧都会检查Leap Motion拾取的任何手势。 frame.gestures包含每只手的数据数组。 frame.gestures[0]表示我们只拿起第一手。 gesture.type将让我们知道选择了什么手势:

var gesture  = frame.gestures[0],
    type = gesture ? gesture.type : '';

如果手势是圆圈,我们将查看是否已发出静音电话的请求。 完成后,我们将callMuteRequestMade设置为true 。 这样一来,我们就不会在盘旋的手指上每帧发送数百个这样的图像。 完成此操作的代码如下所示。

if (type == 'circle') {
  console.log('Circle');

  if (!callMuteRequestMade) {
    // Only ask it to mute once!
    callMuteRequestMade = true;
...

最后,如果这是第一次发出使电话静音的请求,则对我们在服务器上设置的/call路由发出Ajax POST请求。

on {X}代码

我们已经准备好服务器来拦截来自我们的网页和Android设备的呼叫。 我们还准备好发送呼叫静音电话的网页。 最后一点需要设置-我们的Android设备。 我们需要在{X}上创建一条规则,然后将其上传到手机。

对于Android设备,我们将专注于on {X} API中的两个事件处理程序, device.telephony.on('incomingCall')device.telephony.on('idle') 。 每当on {X}在您的设备上检测到来电时,都会触发第一个。 每当设备的电话功能处于空闲状态时(例如电话停止振铃,我们不打出任何电话等),都会触发第二个。

完整的{X}代码如下所示。

var originalCallVolume,
    originalRingerMode,
    currentPhoneNumber,
    textMessageRequested = false;

device.telephony.on('incomingCall', function(signal) {
  originalCallVolume = device.audio.ringerVolume,
  originalRingerMode = device.audio.ringerMode;
  currentPhoneNumber = signal.phoneNumber;

  device.scheduler.setTimer({
    name: 'checkingForInCallInputs', 
    time: 0,
    interval: 5 * 1000,
    exact: false
  }, function() {
    checkIfPhoneShouldBeSilent();
  });
});

device.telephony.on('idle', function() {
  device.scheduler.removeTimer('checkingForInCallInputs');
  returnToPhoneDefaults();
});

function checkIfPhoneShouldBeSilent() {
  device.ajax({
    url: 'http://yourappurlhere.com/shouldibesilent',
    type: 'POST',
    dataType: 'json',
    data: '{"call":"incoming"}',
    headers: {'Content-Type': 'application/json'}
  }, function onSuccess(body, textStatus, response) {
    var JSONResponse = JSON.parse(body);

    console.info('successfully received http response!');
    console.info(JSON.stringify(JSONResponse));

    if (JSONResponse.callSound === false) {
      device.audio.ringerVolume = 0;

      if (!textMessageRequested) {
        textMessageRequested = true;
        device.messaging.sendSms({
          to: currentPhoneNumber,
          body: 'Sorry! In the middle of a technological breakthrough. I\'ll call you back!'
        }, function(err) {
          console.log(err || 'sms was sent successfully');
        });
      }
    }
  }, function onError(textStatus, response) {
    var error = {};

    error.message = textStatus;
    error.statusCode = response.status;
    console.error('error: ',error);
  });
}

function returnToPhoneDefaults() {
  device.audio.ringerVolume = originalCallVolume;
  device.audio.ringerMode = originalRingerMode;
  textMessageRequested = false;

  device.ajax({
    url: 'http://yourappurlhere.com/call',
    type: 'POST',
    dataType: 'json',
    data: '{"action":"reset"}',
    headers: {'Content-Type': 'application/json'}
  }, function onSuccess(body, textStatus, response) {
    var JSONResponse = JSON.parse(body);

    console.info('Successfully got a response after asking to reset the call state');
    console.info(JSON.stringify(JSONResponse));
  }, function onError(textStatus, response) {
    var error = {};

    error.message = textStatus;
    error.statusCode = response.status;
    console.error('error: ',error);
  });
}

来电检测

每当有来电时,我们都会存储电话的当前通话音量和铃声模式。 这样,我们可以在通话结束后将它们重新设置为这些设置,以便以后的通话仍会响起。 我们还存储了呼叫者的电话号码,因此我们可以在将其静音后向他们发送短信:

device.telephony.on('incomingCall', function(signal) {
  originalCallVolume = device.audio.ringerVolume,
  originalRingerMode = device.audio.ringerMode;
  currentPhoneNumber = signal.phoneNumber;
...

然后,我们运行device.scheduler.setTimer() ,它与JavaScript中的本机setTimeout()函数非常相似。 在这里,我们检查电话是否应该每五秒钟保持静音。 其他字段执行以下操作:

  • name :我们将其设置为"checkingForInCallInputs"以便我们有一个名称在以后删除计时器时引用。
  • time :自1970年1月1日以来的时间(以毫秒为单位, Unix时间从该日期开始),您希望计时器从该时间开始。 我们将其设置为零,因为我们希望从现在开始计时。
  • interval :我们希望计时器调用我们的函数多少毫秒的间隔。 我将其设置为每5秒一次。
  • 精确 :将其设置为false可以为重复计时器启用一种电源优化。 我不确定这在多大程度上产生了显着的变化,但认为拥有这套设备不会有什么坏处。

这些字段的device.scheduler.setTimer()函数如下所示:

device.scheduler.setTimer({
  name: 'checkingForInCallInputs', 
  time: 0,
  interval: 5 * 1000,
  exact: false
}, function() {
  checkIfPhoneShouldBeSilent();
});

checkIfPhoneShouldBeSilent()函数相当长,但这是您的典型Ajax请求。 它使用简单的JSON字符串向http://yourappurlhere.com/shouldibesilent发出POST请求,以使我们的服务器知道正在进行传入呼叫。 您需要将URL更改为您自己的服务器的URL。

确保已将dataTypeheaders设置为JSON,以便on {X}以正确的格式发送请求:

dataType: 'json'
headers: {'Content-Type': 'application/json'}

成功从服务器获得响应后,我们将使用JSON.parse(body)解析数据:

onSuccess(body, textStatus, response) {
  var JSONResponse = JSON.parse(body);

然后,我们检查服务器的JSON响应是否表示希望手机静音。 如果是这样,我们使用on {X} API的device.audio.ringerVolume将铃声的音量设置为0:

if (JSONResponse.callSound === false) {
  device.audio.ringerVolume = 0;

我们不想太粗鲁,完全不理会这个人,因此我们可以选择使用on {X} API中的device.messaging.sendSms函数向他们发送SMS。 回想一下,我们将他们的电话号码存储在currentPhoneNumber变量中。 我们还通过将textMessageRequested设置为true来确保仅发送一条SMS:

if (!textMessageRequested) {
  textMessageRequested = true;
  device.messaging.sendSms({
    to: currentPhoneNumber,
    body: 'Sorry! In the middle of a technological breakthrough. I\'ll call you back!'
  }, function(err) {
    console.log(err || 'sms was sent successfully');
  });
}

检测电话何时再次空闲

当电话再次空闲时,我们删除了checkingForInCallInputs计时器:

device.telephony.on('idle', function() {
  device.scheduler.removeTimer('checkingForInCallInputs');

然后,我们运行returnToPhoneDefaults()函数将呼叫音量,振铃器模式和textMessageRequested回其原始值:

function returnToPhoneDefaults() {
  device.audio.ringerVolume = originalCallVolume;
  device.audio.ringerMode = originalRingerMode;
  textMessageRequested = false;

我们还通过使用reset动作向/call发出另一个POST请求来重置服务器,使其不再告诉我们的电话在下一次呼叫时保持静音。 目前,除了调试以外,我们不会将成功响应用于其他任何事情:

device.ajax({
  url: 'http://yourappurlhere.com/call',
  type: 'POST',
  dataType: 'json',
  data: '{"action":"reset"}',
  headers: {'Content-Type': 'application/json'}
}, function onSuccess(body, textStatus, response) {
  var JSONResponse = JSON.parse(body);

  console.info('Successfully got a response after asking to reset the call state');
  console.info(JSON.stringify(JSONResponse));
}, function onError(textStatus, response) {
  var error = {};

  error.message = textStatus;
  error.statusCode = response.status;
  console.error('error: ',error);
});

使用on {X}进行调试

如果您想在任何阶段检查是否有任何效果,可以使用两种方法来调试on {X}代码:

  • 记录您的JSON响应console.info(JSON.stringify(JSONResponse)); 。 您可以通过转到{X}中的“规则”页面,单击规则并选择“日志”标签来查看这些内容。
  • 在手机上创建通知,以显示发生问题的时间device.notifications.createNotification("No longer in a call, I'll stop asking.").show();

最终结果

服务器在Web上运行,网页在浏览器上运行,并且电话已连接到Web,您现在应该可以使来电静音。 为了测试我们的代码,我让我可爱的女友打了我的电话,如下所示:

拨入电话

当电话响起时,我会像这样在空中盘旋手指来静音来电:

将我的手指盘绕在Leap Motion上方以使来电静音

确实会导致电话静音,并且向我的女友发送了一条短信,让她知道我还无法接听电话:

收到短信回复

结论


通过连接各种设备的JavaScript API,可以完成很多有趣的工作。 随着每一个发布JavaScript API的新设备的出现,可能性都将变成互联互通的未来! 走到那里,集成一些API,并做一些以前从未做过的新事情。 如果有人打来电话打扰了您的注意力,请转一下手指,然后重新开始工作。

本教程的所有代码都可以在GitHub找到 。 鼓励读者将其下拉并进行实验。 例如,您可以调整on {X}代码以保持手机静音,或添加对不同手势和动作的支持。 您还可以合并Socket.IO,以提高此处显示的标准Ajax请求的性能。

From: https://www.sitepoint.com/controlling-android-phone-wave-finger/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值