十七、Android 物联网 API 的未来:Android 电视、眼镜、汽车和穿戴

因为不可能涵盖所有的 Android,或者任何可用于 Android 的利基硬件设备 SDK,所以我想我会给你留一章,给你一些关于在你的 Android 硬件设备开发之旅中接下来要看什么的想法。

Android 正在智能地组织这些 API,将 API 功能与硬件产品流派或类型相匹配,例如运行 Android TV 的互动电视集 (iTV 集),运行 Android Glass 的智能眼镜,虽然没有发布但显示在新项目对话框中(参见图 16-28 ,或者运行 Android Auto 的汽车仪表盘,还有运行 Android Wear 的智能手表

我将在明年写一本关于 Android IoT 的书,这本书将涵盖这些其他 API,以及同时可能实现的任何其他物联网 API。随着其他消费电子类型越来越受欢迎,并被主要硬件制造商采用,Android 将发布其他定制的 API。

例如,许多制造商都有虚拟现实耳机,可以运行 Android VR API。Android 驱动的机器人,甚至无人机,越来越受欢迎,所以一个 Android 机器人 API 将允许开发者创建一个定制他们的机器人或无人机的应用。另一个增长的领域是家用电器,所以看到一个 Android Home API 不要惊讶。在这一章中,我将讨论一些其他的 Android APIs,以及一些你应该实现的 Wear 的其他特性。

高清和 UHD 安卓电视:磨损的对立面

Android TV SDK 解决了与 Android Wear SDK 相反的消费电子产品尺寸范围,Android Wear SDK 针对小方形或圆形屏幕进行了优化。Android TV 旨在为大宽屏显示器开发应用,这些显示器具有真正的高清分辨率,或 1920 x 1080 分辨率,或更新的 T2 UHD 分辨率,或 4096 x 2160 T4 分辨率。这些显示器的对角线尺寸从 60 到 80 英寸不等,而智能手表的对角线尺寸从 1 到 2 英寸不等!

合乎逻辑的 Android 电视应用类型包括 2D 和 3D 游戏、直播内容流、电视播放、互动电视和内容搜索。

Android TV 应用需要在 res/drawable-xhdpi 项目文件夹中有一个320 x 180像素的主屏幕横幅,并且应该包括识别应用的本地化文本。这个横幅在 AndroidManifest.xml 应用定义中声明和引用,使用以下参数:

<application android:banner="@drawable/png_banner_name" >
    // Nested Tags for the TV Application will go in here
</application>

打算在 iTV set 设备上运行的 Android TV 应用将需要在 AndroidManifest 中为 Android TV API 声明一个 iTV 启动器活动,使用 <意图过滤器> 子标签中的CATEGORY _ LEANBACK _ Launcher常量。

该意图过滤器将用于识别与电视兼容的 iTV 应用。这是 Google Play 将 iTV set 应用正确分类到商店的 Android TV 应用部分所必需的。该意图还标识了当用户选择 iTV 主屏幕上的图标时要启动的活动。以下 XML 标记显示了如何在清单中编写意图过滤器:

<application android:banner="@drawable/banner" >
  <activity android:name="com.example.android.MainActivity" android:label="@string/app_name" >
    <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
  </activity>
  <activity
    android:name="com.example.android.iTVsetActivity"
    android:label="@string/itv_app_name"
    android:theme="@style/Theme.Leanback">
    <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
    </intent-filter>
  </activity>
</application>

Android 有一个支持 iTV 用户界面设计的库,叫做 V17 后倾库 。这为 iTV 活动提供了标准主题,称为主题。正如你在上面的 XML 标记中看到的那样。

主题。Leanback android: theme 参数可以为 iTV set 应用实现一致的视觉 UI 风格。建议大多数 iTV 应用使用这种向后倾斜主题。主题。Leanback 应与来自 V17 Leanback 库的 iTV UI 设计类一起使用,该库也支持电视媒体播放类。

其他可用于 Android TV 开发的类包括 V7 RecyclerView 库 ,它提供了用于内存高效管理和显示长列表的类。V17 向后倾斜库中的许多类依赖于 V7 RecyclerView 库中的类。还有 V7 CardView 库 ,它为开发人员提供了额外的用户界面小部件,用于显示包含媒体项目图片和媒体项目文本描述的内容信息卡。V7 向后倾斜库也依赖于 V4 支持库,所以就像 Wear 应用一样,许多旧版本的支持库可能需要包含在 Android TV 应用的 build.gradle dependencies 部分中。

您还需要声明您的应用在 Android Manifest XML 文件的 < manifest > 级别使用了一个向后倾斜的用户界面。如果您将此必需的 <用途特征> 属性设置为值,则 Android TV 应用将仅在实现 Android TV 向后倾斜 UI 的 iTV set 设备上运行。对此的 XML 标记如下所示:

<manifest>
    <uses-feature android:name="android.software.leanback" android:required="true" />
    // Other Manifest Child Tags will go in here
</manifest>

如果您正在开发运行在手机、可穿戴设备、平板电脑、电子书阅读器等设备上的应用,除了运行在 Android TV 上之外,您将使用以下标记将这个必需属性设置为一个 false 值:

<manifest>
    <uses-feature android:name="android.software.leanback" android:required="false" />
    // Other Manifest Child Tags will go in here
</manifest>

你打算在 iTV 上运行的 Android 电视应用使用遥控器,不依赖触摸屏进行输入,至少在主要制造商发布触摸屏 iTV 之前是这样,他们可能会在某个时间点发布触摸屏 iTV。

为了向您的用户阐明触摸的使用,您的 iTV 应用清单需要声明此Android . hardware . touch功能不是必需的。这是通过使用 < uses-feature > 标签内的 false 设置来完成的。下面的 XML 标记显示了如何将它包含在 Android 清单应用设置和特权声明文件中:

<manifest>
    <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
    // Other Manifest Child Tags will go in here
</manifest>

此设置会将您的 iTV 应用识别为能够与 Android TV 设备配合使用,并且该应用需要被视为 Android TV 应用并在 Google Play 中的正确 iTV 区域列出。

需要注意的是,目前必须以这种方式声明 iTV 应用,也就是说,在您的应用清单中不需要触摸屏支持,如本示例代码所示,否则您的应用无法出现在电视设备下的谷歌 Play 商店中。

除了互动电视节目,最受欢迎的互动电视应用之一肯定是互动电视游戏。与为纵向设计的智能手机应用或为方形和圆形屏幕设计的智能手表应用不同,Android TV 应用是横向设计的。高清电视(1920×1080)的正常纵横比是 16:9,但具有 2160×1080 分辨率的超宽 16:8 WHD 屏幕也出现了。

与大多数通过网络共享的多人游戏(使用自己的显示器、系统内存和处理器)不同,iTV 游戏不需要任何网络,从而减少了延迟,但它们也共享相同的屏幕、系统内存和多核处理器,因此它们需要精心设计和优化。共享屏幕也可能带来一些独特的多人游戏设计挑战,因为所有玩家都可以看到一切,这在设计依赖玩家专有(未公开)游戏策略信息的纸牌游戏和类似策略游戏时尤其困难。

一个可能的解决方案是一个配套应用,也称为第二屏幕显示应用。这将在蓝牙手机或平板电脑上运行。这将使玩家能够隐藏游戏策略信息,只有他们可以查看,使用第二个屏幕蓝牙连接的应用。

Android TV 主屏幕列出的游戏与常规的 iTV 应用不同。要使一个游戏出现在游戏列表中,请在 Manifest 标签中将 android:isGame 属性设置为“true ”,使用以下标记:

<application android:isGame="true" >

为了告知用户游戏需要或支持游戏控制器,您必须在清单文件中包含以下条目:

<uses-feature android:name="android.hardware.gamepad" />

如果你的 iTV 游戏使用,但不是绝对需要游戏控制器,你将额外包括一个 android:required="false" 参数在< uses-feature >标签中。这个 XML 标记看起来如下所示:

<uses-feature android:name="android.hardware.gamepad" android:required="false" />

Android Auto:汽车仪表盘的 Android 应用

Android Auto SDK (API)平台使您能够扩展您的应用,以便与使用 Android Auto 的汽车控制台系统一起工作。这些 Android 汽车控制台设备 为汽车应用提供基本的 Android 用户界面。这些汽车应用将允许用户在上班、购物、娱乐、约会或公路旅行的路上随身携带应用功能。

就像 Android Wear 一样,适用于 Android 自动控制台的应用可以在蓝牙设备上运行,比如手机或平板电脑。该应用通过特定的 API 与仪表板的 Android 控制台进行通信,该控制台为专为汽车环境内使用而设计的联网 Android Auto 应用提供 Android Auto 用户界面。

Android Auto 应用用户通过语音识别调用的操作和车辆输入控制(如仪表板内的触摸屏,甚至是物理仪表板硬件按钮)与应用和服务进行交互。

Android Auto SDK 目前支持两种类型的应用:音频应用,允许用户在汽车立体声环境中浏览、预览和播放音乐以及语音音频内容,如有声读物;或者消息应用,接收来电通知,使用文本到语音合成大声朗读消息,或者使用汽车或仪表板内置的麦克风通过汽车中的语音输入发送回复。

您可以配置当前为手机和平板电脑开发的音频或消息应用 ,使其在 Android Auto 下工作,而不必适应特定车辆的硬件差异。要使这些应用能够使用 Android Auto,应用必须使用 Android 5.0 API 级别 21 或更高的 targetSdk 设置。应用清单必须声明它使用的汽车功能,如音频播放或消息服务。这与您在 Android Wear 中为 Watch Faces API 执行此操作的方式非常相似。

在应用清单中,您应该提供对 Android Auto XML 配置文件automotive _ app _ desc . XML的引用,您将在 /res/xml 文件夹中创建该文件,就像您对 res/xml/watch_face.xml 文件所做的那样。

为此,您将在<应用>父标签中使用一个 <元数据> 子标签。这个<元数据>标签通过使用以下 xml 标记引用了设置为“com . Google . Android . GMS . car . application”的 android:name 参数和设置为“@xml/automotive_app_desc”的 android:resource 参数:

<application>
    <meta-data android:name="com.google.android.gms.car.application"
     android:resource="@xml/automotive_app_desc"/>
    // Other Application Child Tags will be nested inside of the parent <application> tag
</application>

您将使用放置在项目 XML 资源目录中的 XML 文件来指定汽车应用使用的 Android Auto 功能,您可能需要创建(res/xml)该文件,就像您为 Wear 所做的一样。

要声明一个用于 Android Auto 的音频应用,创建名为 automotive_app_desc.xml 的文件,将其保存在 project res/xml 文件夹下,并确保 xml 定义包含 < automotiveApp ><使用> 标签:

<automotiveApp>
    <uses name="media" />
</automotiveApp>

子标签声明了一个应用打算使用的 Android Auto 特性。如果您的应用需要使用多个 Android Auto SDK 功能,将会添加多个标签。name= " "属性用于指定应用将要利用的功能。

支持的常量包括媒体,用于在车内播放音乐(启用音频应用),以及通知,用于通过汽车概览屏幕显示消息通知,允许用户选择要大声朗读的消息,并允许用户通过语音输入对消息做出响应(启用 Android 自动消息应用)。

对于这个媒体常量,Android 自动控制台设备期望连接到媒体浏览器服务来浏览音轨列表。您应该在清单中声明这个 MediaBrowserService 子类。这将允许汽车的仪表板设备硬件发现服务并连接到应用。

声明 MediaBrowserService 对象以及用于启动它的 Intent 对象所需的 XML 标记看起来很像下面这样:

<application>
    <service android:name=".ProAutoMediaBrowserService" android:exported="true">
        <intent-filter>
            <action android:name="android.media.browse.MediaBrowserService"/>
        </intent-filter>
    </service>
  // Other Application Child Tags will be nested inside of the parent <application> tag
<application>

正如你所看到的,应用提供的用于浏览音轨的ProAutoMediaBrowserService对象必须扩展 AndroidMediaBrowserService类,这将成为 ProAutoMediaBrowserService 类的超类。

Android 汽车硬件设备也使用 Intent 对象作为通知常量,以表明用户已经阅读或回复了应用提供的消息。应用定义阅读或回复消息的意图类型,并将此信息添加到消息通知中,以便仪表板系统可以在用户采取这些操作之一时通知应用。

您需要为应用定义一个 heard 动作意图和一个 reply 动作意图。这些由 BroadcastReceiver 类使用,这些类处理应用清单定义文件中定义的意图。实现这些意图的 XML 标记示例及其关联的 BroadcastReceiver 对象应该如下所示:

<application>
    <receiver android:name=".ProAutoMessageHeardReceiver">
        <intent-filter>
          <action android:name="com.myapp.messagingservice.PRO_AUTO_ACTION_MESSAGE_HEARD"/>
        </intent-filter>
    </receiver>
    <receiver android:name=".ProAutoMessageReplyReceiver">
        <intent-filter>
          <action android:name="com.myapp.messagingservice.PRO_AUTO_ACTION_MESSAGE_REPLY"/>
        </intent-filter>
    </receiver>
</application>

在这个 XML 中,“ProAutoMessageReadReceiver”和“ProAutoMessageReplyReceiver”是您编写来处理您的意图的 BroadcastReceiver 子类的名称。您可以选择将任何您喜欢的常量定义为操作对象名称。确保所有操作名称都是完全唯一的。

构建用于 Android 自动控制台设备的通知将需要 Android V4 支持库中的类。使用 Android SDK 管理器将 Extras > Android 支持库更新到版本 9 或更高版本,并将 Extras > Android 支持库更新到版本 21.0.2 或更高版本(如果有更高版本可用)。

在更新了这些 Android 支持库之后,使用下面的 Groovy 代码,通过将这个依赖项添加到 Android Auto 应用的 build.gradle 文件中,将这些库导入到 Android Studio 开发项目中:

dependencies { compile 'com.android.support:support-v4:21.0.2' }

Android Auto SDK 使用标准的用户界面和用户交互模型,可在所有控制台上工作,因此您无需担心不同的仪表板硬件。Android 非常重视司机分心的问题,它有特定的设计要求,以使汽车应用有资格在谷歌 Play 商店销售。

当用户连接到 Android Auto 时,他们首先会遇到一个概览屏幕,该屏幕根据用户的位置和时间显示上下文卡片。用户可以使用这个屏幕来查看来自消息应用的通知,并选择消息来通过语音输入发送响应。点击活动栏中的耳机图标,查看安装在手持设备上的音频应用。在用户选择音频应用之后,显示器显示该主要应用 UI。主应用 UI 中的媒体控制卡最多支持四个主操作,以及四个辅助操作(在溢出栏上)和返回操作。

Android Auto 使用抽屉 UI 范例进行列表浏览操作,汽车控制台的显示器将使用抽屉转换显示数据列表的内容。对于媒体(音频)应用,自定义的列表 UI 显示应用中媒体服务提供的媒体容器和音频文件。您可以使用列表项目的图标来自定义抽屉中的数据条目。

标准的 Android Auto 用户界面描述了白天和夜间使用的不同配色方案。Android Auto 平台提供状态(白天或夜间)变量,并自动设置该状态。

就 Android 自动颜色主题而言,你的应用可以使用材质主题的调色板中的颜色。材质调色板包含 500 种原色和强调色,可用于插图或开发您的应用颜色方案。它们被设计成彼此和谐地工作,可以在下面的 Google URL 上看到:

http://www.google.com/design/spec/style/color.html

该调色板从 20 种原色开始,即白色、红色、绿色、蓝色、紫色、粉色、靛蓝、青色、蓝绿色、石灰、黄色、琥珀色、橙色、灰色、蓝灰色、棕色、深橙色、深紫色、浅绿色和浅蓝色。如果你认为黑色是一种颜色,实际上总共有 21 种颜色。

其他 480 种颜色填充这个光谱,为 Android 或 WebKit 创建一个完整且可用的调色板。谷歌建议使用 500 种颜色作为你的应用的主要颜色,然后使用其他自定义颜色作为你的强调色。您的 Android Auto 应用将被允许为系统调色板指定两种颜色。这些是在 Android 操作系统中使用 colorPrimaryDarkcolorcentnext数据字段常量定义的。

Android Auto 平台旨在最大限度地提高安全性,减少任何干扰,这个调色板只是这个过程的一小部分。

Android Auto 应用的用户界面需要快速且易于导航,每个 Android Auto 应用都必须通过正式审查,并满足最低安全和驾驶员注意力分散的要求和法规。Android 汽车应用需要完全可预测,并具有内在的直观性,以确保司机始终关注道路,因为生命可能受到威胁。

您的应用需要为 Android Auto 定制。如果没有针对驾驶体验对用户界面、可用性和设计进行调整,就不要在 Android 清单 XML 文件中指定 Android 自动兼容性。复杂的操作,如创建帐户、登录,甚至创建播放列表,都应该在手机或平板电脑上进行,而不是在自动显示器上进行。

Android 汽车应用设计应符合本平台在其汽车平台 UI 中实现的原则,包括简明的 UI 设计和可预测的应用功能,具有持续连接的用户体验,并为用户提供集成的应用环境。

谷歌眼镜:为智能眼镜开发应用

谷歌眼镜开发套件 ,也被称为 GDK,是 Android SDK 的附加软件,可以让你构建直接在谷歌眼镜上运行的玻璃器皿(眼镜应用)。截至本书撰写之时,谷歌眼镜 1 已经停产;然而,谷歌眼镜 2 计划在 2015 年的某个时候发布。如果谷歌聪明的话,它不会让 Glass 2 成为 Android 的外部 API,而是做同样的事情,让它成为一个 Android Glass API 和 SDK,与 Android OS 更加集成,可供所有智能眼镜制造商使用,如 Luxottica、LGE、索尼、six15、Vuzix、三星、GlassUp、PivotHead、Meta AR 和许多其他制造商。

谷歌眼镜 1 的屏幕分辨率为 640 x360。这是四分之一伪高清,因为 1280×720 的屏幕有四个 640×360 的矩形区域。像 Android TV 或 Auto 一样,Glass 有一个独特的 UI 范式,专门适合你的硬件设备。这是围绕时间轴 UI 范例设计的。

该时间线是呈现给用户的主要用户界面。它由 640 x 360 分辨率的卡组成。Glass UI 提供了一些线性功能,例如查看动画和静态卡片、调用语音识别命令以及启动 Android Glass 应用的通用方式。

Android Glass 用户可以在这个时间轴 UI 的不同部分之间线性滚动,显示来自过去、现在和未来的卡片 UI 元素,这些元素等同于屏幕上(现在)、以前查看的(过去)和尚未查看的(未来)。最近查看的(过去的)项目将保持最接近家庭卡。这是用户恢复使用 Android Glass 或者第一次使用时看到的默认卡。

除了导航时间轴卡片,这款 Android Glass UI 还能对用户输入做出响应。用户使用触摸板浏览时间轴 UI,并可以使用语音命令启动 Android Glass 应用。卡片还允许显示菜单项,因此 Android Glass 应用将能够将控制权交给用户,以便他们可以完成操作,例如回复收到的短信,甚至分享照片或视频。

Android Glass 时间轴 UI 及其卡片被组织成多个不同的功能部分,包括首页过去现在未来。一个默认的家庭卡上有一个安卓玻璃时钟。这张 Home 卡占据了时间轴用户界面的中心,每当用户开始使用 Android Glass 设备时,它就会出现。此主页卡保留在时间轴 UI 的中心区域,因为它提供了对 UI 其余部分的访问。

Android Glass 提供语音或触摸命令,允许用户启动 Android Glass 的其他操作系统组件或定制应用。语音命令允许 Android Glass 的免提操作。

Android Glass 主屏幕(时钟)右侧是历史部分,代表过去,显示静态数据卡片。这样做的原因是因为活卡总是被认为是在当下,所以它们永远不会出现在 Android Glass UI 的这个历史部分。

如果不通过重新访问来刷新静态卡,它们会在历史部分自然“衰退”。随着较新的卡片在过去的部分结束,它们将被放置在最靠近主屏幕(时钟)的位置。安卓眼镜会把最老的卡放在最右边。Glass OS 将移除任何超过七天的卡,或者达到 200 张卡的限制。

在 Android Glass 主屏幕(时钟)的左侧是现在未来部分,其中包含静态和动态卡片。实时卡片总是显示与用户相关的实时信息。活卡应该总是出现在现在和未来的版块。当一张活动卡拥有焦点并且 Android Glass 进入睡眠状态时,这张卡将成为 Android Glass 重新开机时出现的默认卡。包含未来“时间戳”或“固定”的静态卡也将出现在当前和未来部分。

例如,Google Now 天气卡可能会自动显示当前和未来部分的相关信息,即使它是一张静态卡。静态信息可以在预定义的时间动态更新。

时间线 UI 的最左侧是设置卡片。有了这些,你就可以配置 Android Glass 操作系统的设置,比如音量或者你正在连接的 Wi-Fi 网络。

Live cards 显然是 Android Glass 的核心功能,因为这些内容总是包含丰富的实时内容,或者用户要求定期更新并保留在屏幕当前区域的内容。Live cards 应该使用自定义图形经常更新,以显示用户引人注目的实时信息。对于需要根据一些用户数据或用户使用的外部数据不断更新的动画用户界面来说,该功能是必需的。

活动卡可以访问加速度计和 GPS 等低级传感器数据。这允许尖端类型的用户交互和特征,这是静态卡所不可能的,因为这样的传感器实时流出动态数据,因此需要动态卡格式。

Live cards 能够在 Android Glass timeline UI 中运行。当这些实时卡中的每一个运行时,用户可以向左或向右滚动以查看其他实时卡并与之交互。这使得用户可以进行多任务处理,并在后台无缝维护每个 live 卡的运行时状态。

静态卡只能显示文本、图像和视频内容,这些内容可以一次性加载到内存中(静态),并且包含不变的数据(非动态)。视频是动画,但它是一系列图像。因此,虽然数字视频看起来是动态的,但实际上不是,因为它不会随着时间的推移而改变,并且每次播放时都是相同的图像系列!静态卡片包含可以用 HTML、文本、图像或视频构建的信息。静态卡不经常更新数据,如果有的话,它们应该为内容显示和快速通知而设计。

静态卡片允许有一个共享菜单项,这允许你的用户与其他人,称为联系人,甚至与另一个 Android Glass 应用共享卡片。您可以将这些 Android Glass timeline UI 卡声明为“可共享”,还可以为 Android Glass 应用定义一个联系人,该联系人可以接受共享的 timeline UI 静态卡项目。

当你需要对用户体验进行全面的用户界面控制时,Android Glass 有一种叫做“immersions ”的东西这些将在典型的 Android Glass timeline 用户界面或用户体验之外运行。

沉浸可以让你呈现自己的用户界面,处理 Android Glass 应用编程逻辑内部的所有用户输入。Android Glass 应用需要沉浸感,这些应用不能在时间轴用户界面范式的严格线性约束下运行,需要自由形式、非线性或面向对象的 GUI 方法。

值得注意的是,时间线卡片和沉浸都可以包含菜单项。它们执行相关的操作,比如回复、配置、删除、共享、数据输入以及更多的用户操作。

最初的 Google Glass 1 API(称为 Mirror API 和 Glassware GDK)并不是 100%特定于 Android 的,尽管它支持 Java 1.6,因此 32 位 Android 4.x. Glass Mirror 也支持 Python 和 PHP。Android Glass API 自然会成为新 Glass 2.0 的一部分,它将是 64 位 Android 5.x 甚至 6.x,并使用 Java 1.7 (Java 7)或 Java 1.8 (Java 8)甚至 Java 1.9 (Java 9)。

因为这个 API 目前正在开发中,还不可用,所以我将在本章的这一节中介绍谷歌眼镜 1 的基础知识,并在我的下一个 Pro Android 标题中介绍 Android 眼镜(或谷歌眼镜 2.0), Pro Android IoT (Apress,2016)。

Android Wear:探索有趣的 API 元素

在本章的其余部分,我将介绍 Android Wear 的一些其他重要功能,这些功能是我在本书前面没有介绍的。你可以在你的表盘代码基础设施上使用其中的大多数,这将允许你制作同时兼容表盘的 Android Wear 应用,这将成为 Android Wear 的一种趋势,因为智能手表硬件设备首先用于数字手表。这就是为什么我确保在本书中完整地介绍了 Watch Faces API。

检测位置:来自 Google Play 服务的 GPS 数据

一些穿戴应用 将需要知道它们的位置,以便能够计算行进的距离(健身和旅行应用),甚至确定用户在地球表面的哪个位置。一些用户,比如我自己,出于隐私原因,倾向于关闭智能手机上的 GPS 功能,除非有令人信服的理由允许自己被跟踪。在考虑使用 GPS 数据的穿戴应用时,也要记住这一点。

许多可穿戴设备包括 GPS 传感器,它可以检索位置数据,而无需绑定到另一个设备,如平板电脑或智能手机。幸运的是,当你在可穿戴应用中请求位置数据时,无需担心这些位置数据来自哪里。Android 可以确定位置数据,并在智能手机或智能手表上使用节能方法更新数据。

穿戴应用应当能够处理位置数据的任何丢失,以防穿戴硬件设备失去与配对设备的连接,并且不碰巧具有 GPS 传感器硬件。

可穿戴设备上的 GPS 位置数据将通过 Google Play 服务(location API)获得,您已经在本书中学习了如何利用该服务。对于 GPS,您将利用 FusedLocationProviderApi 及其附带的类来获取位置数据。要访问 GPS 定位服务,创建您的 GoogleApiClient 实例,这是任何 Google Play 服务 API 的主要入口点,就像您在本书中为 Android Wear Watch Faces API 实现所做的一样。

为了连接到 Google Play 服务,创建一个 GoogleApiClient 的实例,并创建一个活动来指定接口 ConnectionCallbacksOnConnectionFailedListenerLocationListener 的实现。为了管理连接生命周期,在 onResume()方法中调用 connect(),在 onPause()方法中调用 disconnect(),类似于下面的 Java 代码:

public class WearableMainActivity extends Activity implements GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener, LocationListener {
    private GoogleApiClient myGoogleApiClient;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        myGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addApi(Wearable.API)        // used for the data layer API
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();
    }
    @Override
    protected void onResume() {
        super.onResume();
        mGoogleApiClient.connect();
    }
    @Override
    protected void onPause() {
        super.onPause();
        mGoogleApiClient.disconnect();
    }
}

应用连接到 Google Play 服务后,就可以接收位置更新了。当 Android 调用。在客户机的 Connected()回调中,您将通过创建 LocationRequest 对象并使用诸如之类的方法设置选项来构建位置数据请求。setPriority( ) 。您可以使用请求位置更新。onConnected()中的 requestLocationUpdates( ) 或使用删除位置更新。在 onPause()方法 中的 removeLocationUpdates( ) ,可以在下面实现的方法示例代码中看到:

@Override
public void onConnected(Bundle bundle) {
    LocationRequest locationRequest = LocationRequest.create()
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
            .setInterval(UPDATE_INTERVAL_MS)
            .setFastestInterval(FASTEST_INTERVAL_MS);
    LocationServices.FusedLocationApi
            .requestLocationUpdates(myGoogleApiClient, locationRequest, this)
            .setResultCallback(new ResultCallback() {
                @Override
                public void onResult(Status status) {
                    if (status.getStatus().isSuccess()) {
                        if (Log.isLoggable(TAG, Log.DEBUG)) {
                            Log.d(TAG, "Successfully requested location updates");
                        }
                    } else { Log.e(TAG, "Failed in requesting location updates, "
                                        + "status code: " + status.getStatusCode()
                                        + ", message: " + status.getStatusMessage());
                    }
                }
            });
}
@Override
protected void onPause() {
    super.onPause();
    if (myGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.removeLocationUpdates(myGoogleApiClient, this);
    }
    myGoogleApiClient.disconnect();
}
@Override
public void onConnectionSuspended(int i) {
    if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "connection to location client suspended");
    }
}

一旦您启用位置更新,系统会调用一个。onLocationChanged( ) 方法,使用通过指定的间隔值更新位置。setInterval( ) 方法调用。

并非所有智能手表硬件都配备 GPS 传感器。如果用户出去兜兜风,却把手机或平板电脑落在了身后,这款可穿戴应用就无法通过有线连接接收 GPS 定位数据。如果智能手表硬件没有 GPS 传感器,您可以检测到这种情况,并警告用户 GPS 定位功能当前不可用。这是通过使用。hasSystemFeature()方法调用。这个 Java 代码示例轮询一个设备,看它在开始活动时是否有 GPS:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);
    if ( !hasGpsData() ) {
      // Fall-back functionality, for use without location data (Or warn user: No location
data!)
    }
}
private boolean hasGpsData() {
    return getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS);
}

依靠有线连接获取位置数据的智能手表硬件设备也有可能突然失去连接。如果您的智能手表应用处理持续的 GPS 数据流,您将需要根据数据中断或变得不可用的位置来处理断开连接。在没有 GPS 传感器的智能手表硬件上,当智能手表失去其有线数据连接时,可能会丢失 GPS 位置数据。

如果您的应用依赖有线数据连接来获取位置数据,而穿戴设备没有 GPS 传感器,您应该检测连接丢失,警告用户,并更改应用的功能。

为了检测一个连接数据的丢失,你需要扩展一个 WearableListenerService。此服务将允许您处理数据层事件。为此,在 AndroidManifest 中声明一个,通知 Android 有关 WearableListenerService 的信息。此过滤器将允许操作系统在需要时绑定到您的 WearableListenerService。

定义 ListenerService 类的父标签和 BIND_LISTENER 子标签< intent-filter >的 XML 标记应该如下所示:

<service android:name=".MyNodeListenerService">
    <intent-filter>
        <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
    </intent-filter>
</service>

在 WearableListenerService 的 MyNodeListenerService 子类中,您可以实现。onPeerDisconnected( ) 方法 用于处理智能手表硬件设备是否内置 GPS 的情况。Java 代码如下所示:

public class MyNodeListenerService extends WearableListenerService {
    @Override
    public void onPeerDisconnected(Node peer) {
        if( !hasGpsData() ) {
          // Fall-back to functions that don't use location or notify user to bring their
handset
        }
    }
}

如果它丢失了 GPS 数据(信号),您应该使用。getLastLocation()方法调用。在无法获取 GPS 数据的情况下,或者智能手表没有内置 GPS 功能,并且与手机或平板电脑失去连接时,这种方法会很有帮助。下面是如何使用?getLastLocation()方法调用(记住在您的清单 XML 文件中请求 GPS 定位权限)以便检索最近已知的 GPS 定位数据(如果可用的话):

Location location = LocationServices.FusedLocationApi.getLastLocation(myGoogleApiClient);

如果您的智能手表应用使用手表上的 GPS 记录数据,您也可以将智能手表位置数据与智能手机位置数据同步。使用 LocationListener,您可以实现一个。onLocationChanged( ) 方法

@Override
public void onLocationChanged(Location location) {
    addLocationEntry(location.getLatitude(), location.getLongitude());
}
private void addLocationEntry(double latitude, double longitude) {
    if (!mySavedGpsLocation || !myGoogleApiClient.isConnected()) { return; }
    mCalendar.setTimeInMillis(System.currentTimeMillis());
    String path = Constants.PATH + "/" + mCalendar.getTimeInMillis();
    PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(path);
    putDataMapRequest.getDataMap().putDouble(Constants.KEY_LATITUDE, latitude);
    putDataMapRequest.getDataMap().putDouble(Constants.KEY_LONGITUDE, longitude);
    putDataMapRequest.getDataMap().putLong(Constants.KEY_TIME, mCalendar.getTimeInMillis());
    PutDataRequest request = putDataMapRequest.asPutDataRequest();
    Wearable.DataApi.putDataItem(myGoogleApiClient, request)
            .setResultCallback(new ResultCallback() {
                @Override
                public void onResult(DataApi.DataItemResult dataItemResult) {
                    if (!dataItemResult.getStatus().isSuccess()) {
                        Log.e(TAG, "Failed to set the data, "
                                + "status: " + dataItemResult.getStatus()
                                .getStatusCode());
                    }
                }
            });
}

另一个在 Wear 智能手表硬件中越来越受支持的主要功能是一个麦克风,接下来我们来看看语音识别!

语音动作:使用语音识别技术

我们都记得(或者我们记得吗?)先锋迪克·特雷西(什么时候会有人把这个翻拍成卖座片?)漫画系列,主人公迪克·特雷西能够对着他的智能手表说话,引发奇迹,总能化险为夷。

现在你也可以让你的用户与你的智能手表应用对话,让它为他们创造奇迹,因为语音动作已经成为 Android Wear 用户体验的一个不可或缺的部分,允许用户不用手就能相对快速地触发应用动作。

Android Wear 允许开发人员进行两种类型的语音操作。系统提供的语音动作基于操作系统任务,内置于 Wear 平台。当使用 Android 中的语音识别引擎调用特定的语音动作时,您可以使用 IntentFilter 对象为您希望启动的活动定义它们,我将在稍后讨论这一点。系统提供的语音操作的一些例子包括“记笔记”、“设置闹钟”或“叫出租车”,甚至“启动秒表”等等。

第二种语音动作是应用提供的语音动作。这些语音操作是基于应用的,您可以像启动器图标一样声明它们。为了利用这些动作,用户将声明“start”和一个动作(指定为标签),以便利用语音动作,并且您指定的活动子类将随后启动(Start)。

Android Wear 平台提供了许多基于用户动作的语音意图常量,允许用户说出他们想要做的事情,并让系统计算出要启动的最佳活动。这些包含在 android.speech 包的 RecognizerIntent 类中,如果您想研究它们,可以在下面的 android 开发人员网站 URL 中找到:

http://developer.android.com/reference/android/speech/RecognizerIntent.html

当用户调用语音操作时,您的应用可以过滤用于启动相关活动的意图,如您的清单中所定义的。

如果您想启动一个服务,将活动显示为可视提示,并在该活动中启动服务。务必打电话给。当你想去掉活动(服务)的可视提示包装器时。

例如,应该用来实现“Take a Note to Self”命令的 XML 标记块在将启动名为 SelfNote 的活动子类的父中声明了子,如下所示:

<activity android:name="SelfNoteActivity">
  <intent-filter>
    <action android:name="android.intent.action.SEND" />
    <category android:name="com.google.android.voicesearch.SELF_NOTE" />
  </intent-filter>
</activity>

如果没有任何语音意图常量适用于您的应用,您可以使用自定义的“开始活动[此处输入名称]”语音操作直接启动您的应用。注册应用以使用自定义“开始”动作的方法与注册启动器图标的方法相同。不是在启动器 XML 结构中请求应用图标,而是一个应用将请求一个语音动作,使用 android:label 参数。要指定用户在“开始:”后使用的语音命令,请为您希望启动的活动指定标签属性。例如,这个<意图过滤器>构造识别“开始:语音控制应用”语音动作,启动 StartVoiceActivity:

<application>
  <activity android:name="StartVoiceActivity" android:label="VoiceControlApp">
    <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
  </activity>
</application>

除了结合 Android Wear 中的内置语音识别常量使用语音操作来启动活动,您还可以使用 SpeechRecognizer 类来处理“自由形式”或非标准的语音输入,该类访问识别服务类和识别意图类,后者访问前面提到的预定义语音操作常量。

这些只是 android.speech 包的部分组件。这个语音识别器类和相关类将被用来从用户那里获得免提语音输入,并处理该语音输入,提供一个结果。

这方面的一个例子可能需要发送短文本消息或为搜索引擎指定搜索项。值得注意的是,语音 API 并不打算用于连续语音识别,例如您可以使用专业语音软件包,如 Nuance Communications 的 Dragon Naturally Speaking Premium 软件。微软 Windows 8.1 还内置了语音识别功能。

你不想用 Android Wear 进行连续语音识别的原因是,尽管 Android 操作系统非常强大,但它仍然只是一个嵌入式设备操作系统,目前不具备执行连续语音识别和翻译的 CPU 内核、内存和时钟速度。

试图在 Wear 应用内完成这种级别的语音识别将消耗大量的电池寿命、处理开销和网络带宽。SpeechRecognizer 方法需要使用主应用线程来调用。您使用一个 ACTION_RECOGNIZE_SPEECH 操作调用 startActivityForResult( ) ,启动一个语音识别活动。然后,您将使用下面的 Java 结构在 onActivityResult()方法中处理结果:

private static final int SPEECH_REQUEST_CODE = 0;
private void displaySpeechRecognizer() {
    Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);     // Create an Intent object
    intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                    RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
    startActivityForResult(intent, SPEECH_REQUEST_CODE);               // Start the Activity
}
@Override // The callback which is invoked when the Speech Recognizer returns
protected void onActivityResult(int requestCode, int resultCode, Intent data) {    // Process Intent    if (requestCode == SPEECH_REQUEST_CODE && resultCode == RESULT_OK) {
        List<String> results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
        String spokenWord = results.get(0);
        // Process Voice Recognition Result
    }
    super.onActivityResult(requestCode, resultCode, data);
}

添加位置感知和语音识别可以让您的应用感知!

摘要

恭喜你,你已经完成了这本专业的 Android 可穿戴设备书籍,并获得了成功!您正在创建创新的穿戴应用,并准备好引发这场物联网革命!

在这最后一章中,除了 Wear 之外,您还了解了 Android OS 最近引入的一些额外的物联网 API。你看了一下 Android TV,它允许你为用户现在安装在他们客厅的 64 到 96 英寸的大屏幕创建应用。

接下来,您了解了新的 Android Auto API,它允许您创建在汽车仪表板环境中无缝工作的应用,并利用内置的汽车硬件,如音频扬声器。

你还看了一下谷歌眼镜 1,即将成为 Android 眼镜 2,以及 Android Wear 的其他一些你应该考虑使用的功能。

接下来,您看了一下使用 GPS 传感器硬件数据、Google 移动服务(GMS)和 Google Play 服务实现基于位置的服务。

本章最后介绍了最新语音识别技术的使用,以及 Android 提供的内部语音动作命令。