使应用程序具有上下文意识是向用户提供有用服务的最佳方法之一。 尽管还有多种方法可以做到这一点,包括地理围栏 , 活动识别和其他位置服务,但 Google最近发布了Awareness API ,该API允许开发人员创建可以对用户的现实情况做出智能反应的应用程序。 Awareness API结合了Places API ,Locations API,Activity Recognition和Nearest API,并增加了对耳机状态和天气检测的支持。
在本教程中,您将了解Awareness API,以及如何访问数据快照 ,以及如何为符合应用程序目标的用户条件组合创建侦听器(称为fences ,其名称来自地理围栏)。 这对于各种各样的应用程序很有用,例如基于位置的游戏,向商店中的用户提供优惠券以及在检测到用户运动时启动音乐应用程序。 该示例应用程序的所有代码都可以在GitHub上找到。
1.设置开发者控制台
在进入您的Android应用程序之前,您需要通过Google API控制台设置Google Play服务。 如果您已经创建了一个项目,则可以跳过本节的第一步。 如果没有,您可以单击上面的链接并继续为您的应用程序创建一个新项目。
步骤1:建立专案
要创建一个新项目,请单击屏幕顶部中心的蓝色“ 创建项目”按钮。

这为您提供一个对话框,询问您的项目名称。 在本教程中,我创建了一个名为TutsPlusAwareness的项目。 由于只能使用字母,数字,引号,连字符,空格和感叹号,因此对项目命名有一些限制。

一旦点击Create ,页面的右下角将出现一个对话框,指示正在创建项目。 消失后,您的项目即可进行设置。 您应该看到类似于以下的屏幕。 如果不是,请单击左上角的Google API徽标,以转到API管理器屏幕。

步骤2:启用必要的API
在Google API概述屏幕上,选择搜索框,然后搜索Awareness API。

从返回的结果中选择Awareness API后,单击蓝色的“ 启用”按钮以允许您的应用使用Awareness API。 如果这是您启用的第一个API,系统将提示您创建一组凭据。 继续执行本教程第3步的“ 凭据”页面。

除了Awareness API外,您可能还需要启用其他API。 如果您的应用使用了Awareness API的Places功能,则需要启用Android版Google Places API 。

如果您的应用使用信标,则还需要启用附近消息API 。
步骤3:建立Android API金钥
为了使用启用的API,您将需要为您的Android应用生成一个API密钥。 在您的Google项目的凭据页面上 ,从顶部的下拉菜单中选择Awareness API ,然后从第二个菜单中选择Android 。

接下来,将转到一个屏幕,您可以在其中输入应用程序的程序包名称和SHA1证书作为应用程序的签名密钥。 为了在Linux或OS X上获得调试密钥的签名密钥,请在终端窗口中输入以下命令。
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
在Windows上,可以将路径设置为debug.keystore文件的位置来运行同一命令。

单击“ 创建API密钥”按钮后,您将获得在Android应用程序中需要使用的API密钥。

2.设置Android项目
创建了API密钥并启用了正确的API之后,就可以开始设置Android项目了。 在本教程中,我们将创建一个测试应用程序以学习API。
为了演示Awareness API的所有功能,本教程将集中于使用一个简单的列表来表示所使用的每个功能。 尽管将不讨论创建此列表的详细信息,但是您可以在GitHub源中找到本教程的完整实现。
步骤1:设定Play服务
首先,您需要在build.gradle文件中包含Play服务库。 虽然可以包括所有Google Play服务,但最好仅包括应用所需的软件包。
在这种情况下,Awareness API在ContextManager包中可用,并且可以通过在依赖项节点中添加以下行来将其包含在您的项目中。 您还需要确保包含AppCompat库 ,因为该库将用于检查棉花糖及以上设备的权限。
compile 'com.google.android.gms:play-services-contextmanager:9.2.0'
compile 'com.android.support:appcompat-v7:23.4.0'
添加以上行后,同步您的项目并打开您的项目的strings.xml文件。 您需要将Google API控制台中的API密钥放入字符串值中。
<string name="google_play_services_key">YOUR API KEY HERE</string>
添加API密钥后,您将需要打开项目的AndroidManifest.xml文件。 根据您所使用的Awareness API的功能,您将需要包含应用程序的权限。 如果您的应用使用了Awareness API的信标,位置,地点或天气功能,则您需要包括 ACCESS_FINE_LOCATION
权限。 如果您需要使用活动识别功能,则将需要ACTIVITY_RECOGNITION
权限。
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
接下来,您需要在将您的应用程序与Google API服务相关联的application
节点内声明meta-data
。 根据您的应用使用的内容,您还需要包括com.google.android.geo
和com.google.android.nearby
元数据,以使用位置,信标和位置功能。
<meta-data
android:name="com.google.android.awareness.API_KEY"
android:value="@string/google_play_services_key" />
<!-- places/location declaration -->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/google_play_services_key" />
<!-- Beacon snapshots/fences declaration -->
<meta-data
android:name="com.google.android.nearby.messages.API_KEY"
android:value="@string/google_play_services_key" />
接下来,您将需要打开MainActivity.java文件。 将GoogleApiClient.OnConnectionFailedListener
接口添加到您的类中,并在onCreate(Bundle)
方法中连接到Google Play服务和Awareness API。
public class MainActivity extends AppCompatActivity implements
GoogleApiClient.OnConnectionFailedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Awareness.API)
.enableAutoManage(this, this)
.build();
mGoogleApiClient.connect();
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {}
}
步骤2:权限
现在,您已在Android应用程序中配置了Play服务,您将需要确保Android Marshmallow或更高版本上的用户已授予您的应用程序使用其位置的权限。 您可以在onCreate(Bundle)
以及在访问任何需要位置权限的功能之前检查此权限,以避免应用程序崩溃。
private boolean checkLocationPermission() {
if( !hasLocationPermission() ) {
Log.e("Tuts+", "Does not have location permission granted");
requestLocationPermission();
return false;
}
return true;
}
private boolean hasLocationPermission() {
return ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION )
== PackageManager.PERMISSION_GRANTED;
}
如果尚未授予位置许可,则可以请求用户授予它。
private final static int REQUEST_PERMISSION_RESULT_CODE = 42;
private void requestLocationPermission() {
ActivityCompat.requestPermissions(
MainActivity.this,
new String[]{ Manifest.permission.ACCESS_FINE_LOCATION },
REQUEST_PERMISSION_RESULT_CODE );
}
这将导致出现系统对话框,询问用户是否要向您的应用授予权限以知道其位置。

用户响应后, onRequestPermissionsResult()
回调将接收结果,并且您的应用程序可以相应地响应。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
@NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_PERMISSION_RESULT_CODE: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//granted
} else {
Log.e("Tuts+", "Location permission denied.");
}
}
}
}
至此,您应该完成设置应用程序以使用Awareness API。
3.使用快照API
当您想收集有关用户当前上下文的信息时,可以使用Awareness API的快照功能。 此API会根据发出的API调用的类型来收集信息,并将缓存此信息以便在各种应用程序之间快速访问。
头戴式耳机
通过Awareness API向Play服务提供的新功能之一是能够检测设备的耳机状态(插入或拔出)。 这可以通过在Awareness API上调用getHeadphoneState()
并从HeadphoneStateResult
读取HeadphoneState
来完成。
private void detectHeadphones() {
Awareness.SnapshotApi.getHeadphoneState(mGoogleApiClient)
.setResultCallback(new ResultCallback<HeadphoneStateResult>() {
@Override
public void onResult(@NonNull HeadphoneStateResult headphoneStateResult) {
HeadphoneState headphoneState = headphoneStateResult.getHeadphoneState();
if (headphoneState.getState() == HeadphoneState.PLUGGED_IN) {
Log.e("Tuts+", "Headphones are plugged in.");
} else {
Log.e("Tuts+", "Headphones are NOT plugged in.");
}
}
});
}
知道耳机的状态后,您的应用程序即可根据该信息执行所需的任何操作。
位置
尽管以前可作为Google Play服务的组件使用,但Awareness API的位置功能已针对效率和电池使用进行了优化。 您可以像这样请求一次位置快照,而不是使用传统的Location API并以指定的时间间隔接收位置。
private void detectLocation() {
if( !checkLocationPermission() ) {
return;
}
Awareness.SnapshotApi.getLocation(mGoogleApiClient)
.setResultCallback(new ResultCallback<LocationResult>() {
@Override
public void onResult(@NonNull LocationResult locationResult) {
Location location = locationResult.getLocation();
Log.e("Tuts+", "Latitude: " + location.getLatitude() + ", Longitude: " + location.getLongitude());
Log.e("Tuts+", "Provider: " + location.getProvider() + " time: " + location.getTime());
if( location.hasAccuracy() ) {
Log.e("Tuts+", "Accuracy: " + location.getAccuracy());
}
if( location.hasAltitude() ) {
Log.e("Tuts+", "Altitude: " + location.getAltitude());
}
if( location.hasBearing() ) {
Log.e("Tuts+", "Bearing: " + location.getBearing());
}
if( location.hasSpeed() ) {
Log.e("Tuts+", "Speed: " + location.getSpeed());
}
}
});
}
如您所见,您首先需要验证用户是否已授予位置权限。 如果有,则可以检索标准的Location对象,其中包含有关用户的位置和速度的大量数据以及有关此数据准确性的信息。
由于某些数据可能不可用,您将需要在使用前验证特定信息是否存在。 运行此代码应将所有可用数据输出到Android系统日志。
E/Tuts+: Latitude: 39.9255456, Longitude: -105.02939579999999
E/Tuts+: Provider: Snapshot time: 1468696704662
E/Tuts+: Accuracy: 20.0
E/Tuts+: Altitude: 0.0
E/Tuts+: Speed: 0.0
地方
尽管Awareness API不如标准的Places API健壮,但确实提供了一种快速,易于使用的方式来收集有关用户附近地点的信息。 此API调用将返回PlaceLikelihood
对象的List
, PlaceLikelihood
对象包含一个Place
和一个float
代表用户在该位置的可能性(因此,对象的名称)。
每个Place
对象可能包含姓名,地址,电话号码,地点类型,用户评价,以及其他有用的信息。 您可以在确认用户已授予位置权限后,为该用户请求附近的地点。
private void detectNearbyPlaces() {
if( !checkLocationPermission() ) {
return;
}
Awareness.SnapshotApi.getPlaces(mGoogleApiClient)
.setResultCallback(new ResultCallback<PlacesResult>() {
@Override
public void onResult(@NonNull PlacesResult placesResult) {
Place place;
for( PlaceLikelihood placeLikelihood : placesResult.getPlaceLikelihoods() ) {
place = placeLikelihood.getPlace();
Log.e("Tuts+", place.getName().toString() + "\n" + place.getAddress().toString() );
Log.e("Tuts+", "Rating: " + place.getRating() );
Log.e("Tuts+", "Likelihood that the user is here: " + placeLikelihood.getLikelihood() * 100 + "%");
}
}
});
}
运行上述方法时,您应该在Android控制台中看到类似于以下内容的输出。 如果数字不可用,则返回-1
。
E/Tuts+: North Side Tavern
12708 Lowell Blvd, Broomfield, CO 80020, USA
E/Tuts+: Rating: 4.7
E/Tuts+: Likelihood that the user is here: 10.0%
E/Tuts+: Quilt Store
12710 Lowell Blvd, Broomfield, CO 80020, USA
E/Tuts+: Rating: 4.3
E/Tuts+: Likelihood that the user is here: 10.0%
E/Tuts+: Absolute Floor Care
3508 W 126th Pl, Broomfield, CO 80020, USA
E/Tuts+: Rating: -1.0
天气
通过Awareness API向Google Play服务提供的另一项新功能是能够获取用户的天气状况。 此功能还要求棉花糖及更高版本上的用户具有位置权限。
使用此请求,您将能够以华氏度或摄氏温度获得用户区域中的温度。 您还可以找到温度,露点(空气中的水开始凝结成露水的温度),湿度百分比和天气状况。
private void detectWeather() {
if( !checkLocationPermission() ) {
return;
}
Awareness.SnapshotApi.getWeather(mGoogleApiClient)
.setResultCallback(new ResultCallback<WeatherResult>() {
@Override
public void onResult(@NonNull WeatherResult weatherResult) {
Weather weather = weatherResult.getWeather();
Log.e("Tuts+", "Temp: " + weather.getTemperature(Weather.FAHRENHEIT));
Log.e("Tuts+", "Feels like: " + weather.getFeelsLikeTemperature(Weather.FAHRENHEIT));
Log.e("Tuts+", "Dew point: " + weather.getDewPoint(Weather.FAHRENHEIT));
Log.e("Tuts+", "Humidity: " + weather.getHumidity() );
if( weather.getConditions()[0] == Weather.CONDITION_CLOUDY ) {
Log.e("Tuts+", "Looks like there's some clouds out there");
}
}
});
}
上面的代码应输出与此类似的内容。
E/Tuts+: Temp: 88.0
E/Tuts+: Feels like: 88.0
E/Tuts+: Dew point: 50.0
E/Tuts+: Humidity: 28
E/Tuts+: Looks like there's some clouds out there
这里要注意的一件事是天气条件值存储为int
。 条件值的完整列表可以在“天气”对象中找到。
int CONDITION_UNKNOWN = 0;
int CONDITION_CLEAR = 1;
int CONDITION_CLOUDY = 2;
int CONDITION_FOGGY = 3;
int CONDITION_HAZY = 4;
int CONDITION_ICY = 5;
int CONDITION_RAINY = 6;
int CONDITION_SNOWY = 7;
int CONDITION_STORMY = 8;
int CONDITION_WINDY = 9;
活动
用户的活动将在他们与设备交互的方式中起很大的作用,并且检测到该活动将使您提供更流畅的用户体验。
例如,如果您有健身应用程序,则可能希望检测用户何时跑步,以便开始记录Google Fit会话 ,或者如果您检测到用户仍在使用健身程序,则可能希望向其发送通知。白天太多时间。
使用Awareness API中的getDetectedActivity()
调用,您可以获得可能的活动的列表以及用户进行每个活动的时间。
private void detectActivity() {
Awareness.SnapshotApi.getDetectedActivity(mGoogleApiClient)
.setResultCallback(new ResultCallback<DetectedActivityResult>() {
@Override
public void onResult(@NonNull DetectedActivityResult detectedActivityResult) {
ActivityRecognitionResult result = detectedActivityResult.getActivityRecognitionResult();
Log.e("Tuts+", "time: " + result.getTime());
Log.e("Tuts+", "elapsed time: " + result.getElapsedRealtimeMillis());
Log.e("Tuts+", "Most likely activity: " + result.getMostProbableActivity().toString());
for( DetectedActivity activity : result.getProbableActivities() ) {
Log.e("Tuts+", "Activity: " + activity.getType() + " Likelihood: " + activity.getConfidence() );
}
}
});
}
上面的方法将为用户显示最可能的活动,处于该状态的时间以及所有可能的活动的列表。
E/Tuts+: time: 1468701845962
E/Tuts+: elapsed time: 15693985
E/Tuts+: Most likely activity: DetectedActivity [type=STILL, confidence=100]
E/Tuts+: Activity: 3 Likelihood: 100
DetectedActivity
类型的值可以映射到以下值:
int IN_VEHICLE = 0;
int ON_BICYCLE = 1;
int ON_FOOT = 2;
int STILL = 3;
int UNKNOWN = 4;
int TILTING = 5;
int WALKING = 7;
int RUNNING = 8;
信标
快照和最困难的最后一种类型的设置,因为它需要一个真实的组件包括BLE(B luetooth 大号流ËNERGY)信标。 尽管“附近的API”不在本教程的讨论范围之内,但是您可以使用Google的“信标工具”应用为自己的Google Services项目初始化信标。

要注意的重要一点是,一旦您将信标注册到Google API项目, 就无法在不重置信标ID的情况下取消注册 。 这意味着,如果您删除该项目,则需要使用制造商的应用程序重新配置信标。 对于Awareness API,名称空间必须与您用于Awareness API调用的Google项目匹配。 上面的信标已经注册到一个个人测试的Google项目中,因此与本教程相关的示例项目的名称空间( reflected-disk-355
)不同。
在上面的屏幕截图中,您可以在“ 附件”下看到一项。 此附件的名称空间是反映磁盘-355(本教程中的示例项目的命名空间是tutsplusawareness)和类型就在附近 。 您将需要自己的信标使用此信息,以便使用Awareness API检测附件。
配置信标后,可以返回代码。 您将需要创建一个BeaconState.TypeFilter
对象List
,以便您的应用程序可以过滤掉与您的应用程序无关的信标和附件。
private static final List BEACON_TYPE_FILTERS = Arrays.asList(
BeaconState.TypeFilter.with(
//replace these with your beacon's values
"namespace",
"type") );
如果您有理由相信用户在信标附近,则可以从信标中请求符合上述过滤器要求的附件。 这将要求棉花糖及更高版本上的用户具有位置权限。
private void detectBeacons() {
if( !checkLocationPermission() ) {
return;
}
Awareness.SnapshotApi.getBeaconState(mGoogleApiClient, BEACON_TYPE_FILTERS)
.setResultCallback(new ResultCallback<BeaconStateResult>() {
@Override
public void onResult(@NonNull BeaconStateResult beaconStateResult) {
if (!beaconStateResult.getStatus().isSuccess()) {
Log.e("Test", "Could not get beacon state.");
return;
}
BeaconState beaconState = beaconStateResult.getBeaconState();
if( beaconState == null ) {
Log.e("Tuts+", "beacon state is null");
} else {
for(BeaconState.BeaconInfo info : beaconState.getBeaconInfo()) {
Log.e("Tuts+", new String(info.getContent()));
}
}
}
});
}
此代码将注销与上述示例信标关联的附件信息。 对于此示例,我已经配置了两个具有相同名称空间和类型的信标,以演示可以一次检测到多个信标。
E/Tuts+: Oh hi tuts+
E/Tuts+: Portable Beacon info
4.使用Fences API
尽管Snapshot API可以在特定时间获取有关用户上下文的信息,但是Fences API会在允许执行操作之前侦听要满足的特定条件。 Fences API已针对提高电池和数据的使用效率进行了优化,以便对您的用户礼貌。
创建围栏时可以检查五种类型的条件:
目前,天气条件和地点均不支持围栏。 您可以使用任何支持的功能制作围栏; 但是,此API真正方便的功能是可以将逻辑运算应用于条件。 您可以使用多个围栏,并不能使用和 , 或来组合条件来满足应用程序的需求。
创建一个BroadcastReceiver
在创建围栏之前,您需要具有一个代表应用程序将监听的每个围栏的键值。 为了完成本教程,您将构建一个围栏,以检测用户何时坐在设置的位置(例如家中)。
private final static String KEY_SITTING_AT_HOME = "sitting_at_home";
一旦定义了键,就可以收听包含该键的广播Intent
。
public class FenceBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if(TextUtils.equals(ACTION_FENCE, intent.getAction())) {
FenceState fenceState = FenceState.extract(intent);
if( TextUtils.equals(KEY_SITTING_AT_HOME, fenceState.getFenceKey() ) ) {
if( fenceState.getCurrentState() == FenceState.TRUE ) {
Log.e("Tuts+", "You've been sitting at home for too long");
}
}
}
}
}
创建围栏
现在您已经创建了一个接收器来监听用户事件,现在该创建围栏了。 您将创建的第一个AwarenessFence
将在用户处于STILL
状态时进行监听。
AwarenessFence activityFence = DetectedActivityFence.during(DetectedActivityFence.STILL);
您将创建的第二个围栏将等待用户位于特定位置的范围内。 尽管此样本已设置了纬度和经度值,但是您将需要更改它们以匹配与您的位置相匹配的任何坐标以进行测试。
AwarenessFence homeFence = LocationFence.in(39.92, -105.7, 100000, 1000 );
现在您已经有了两个栅栏,您可以通过使用AwarenessFence.and
操作将它们组合在一起以创建第三个AwarenessFence
。
AwarenessFence sittingAtHomeFence = AwarenessFence.and(homeFence, activityFence);
最后,您可以创建一个PendingIntent
,该消息将广播到BroadcastReceiver
并使用updateFences
方法将其添加到Awareness API。
Intent intent = new Intent(ACTION_FENCE);
PendingIntent fencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
mFenceBroadcastReceiver = new FenceBroadcastReceiver();
registerReceiver(mFenceBroadcastReceiver, new IntentFilter(ACTION_FENCE));
FenceUpdateRequest.Builder builder = new FenceUpdateRequest.Builder();
builder.addFence(KEY_SITTING_AT_HOME, sittingAtHomeFence, fencePendingIntent);
Awareness.FenceApi.updateFences( mGoogleApiClient, builder.build() );
现在,当用户坐在指定位置范围内时,该应用程序将记录一条消息。
结论
在本教程中,您了解了Awareness API以及如何收集有关用户环境的当前信息。 您还学习了如何为用户的上下文中的更改注册侦听器,以及如何在满足特定条件时采取行动。
有了这些信息,您应该能够扩展自己的应用程序,并根据他们当前的位置,活动和其他有用的价值为用户提供更多惊人的体验。
翻译自: https://code.tutsplus.com/tutorials/google-play-services-awareness-api--cms-25858