文档

1. 创建一个Fitwear应用

一个Fitwear应用由两部分组成:1) 运行在手表端的APK;2) 运行在手机端的应用。通常情况下,运行在手表端的APK会被打包在手机端应用中,在安装时会被自动推送到手表上。所以将Fitwear应用上传到Fitwear应用商店时只需要上传手机端应用即可。在这里,我们将一步步教你如何创建一个简单的Fitwear应用。

2. 下载SDK

在开发应用前,将安卓SDK版本升级到最新版本, 可以获取最新的API支持。

手表端Fitwear系统兼容Android 5.1的API,所以开发者可以基于Android 5.1的SDK开发手表端的应用,该应用可以直接在Fitwear系统上运行。但是当开发者在开发手机端的应用时则应和普通的安卓应用一样,依据用户的手机安卓系统版本来确定支持的API。

3. 配置开发环境

在应用市场上下载最新版本的Fitwear助手,安装在手机上。手机安卓系统必须为安卓4.3以上版本。将手表和手机均连接到电脑上。

4. 创建项目

用 Android Studio 或者 Eclipse 均可以创建开发项目,强烈建议使用Android Studio来开发,会方便很多。下面以Android Studio举例说明,在创建项目过程中,点击 File -> New Project ,按照建立程序向导的提示,进行以下步骤的操作:

1.在Configure your new project窗口,输入应用名和包名。

2.在Form factors窗口:

选择Phone and Tablet并选择API 18: Android 4.3 (Jelly Bean) 作为Minimum SDK。

选择Wear并选择API 20: Android 4.4 (KitKat Wear) 作为Minimum SDK。

3.在第一个Add an activity to Mobile窗口,为手机移动设备添加一个空白Activity。

4.在第二个Add an activity to Wear窗口,为手表设备添加一个空白Activity。

5.下载hll-api.jar,添加到项目的库依赖中。

当结束创建向导以后,Android Studio会创建一个包含两个模块的新工程。你现在可以为手机端和手表端的两个应用创建Activity、Service、UI等等。在手机端应用,一般做一些比较重的任务,例如网络连接,复杂的计算或者一些需要复杂用户交互的任务。当在手机端完成任务以后,可以把手机端的处理结果通过数据传输API通知给手表端。

5. 安装手表端应用

将生成的手表端APK通过adb install的方式即可安装到手表端。也可以通过 Android Studio 或者 Eclipse 直接将手表端APK安装到手表端。

6. 打包应用的注意事项

1.创建一个 res/xml/wearable_app_desc.xml 文件,包括手表端应用的版本和路径信息。例如:(如果使用Android studio 开发,这一块不考虑直接看2


							

注:这个XML文件非常关键,请保证:

a.rawPathResId里的名称,与你放到res/raw下的手表APK名称一致。

b.versionCode、versionName与手表APK一致。

c.package与手机、手表一致。

2.添加一个 meta-data 标签在手机端app的中,声明 wearable_app_desc.xml 的路径。


							

3.打包,使用与手表端应用相同的签名对最终的手机端APK进行签名。

4.最后得到的手机端APK即为符合要求可以上传应用商店的应用。

7. 混淆配置

如果你对应用进行了混淆,请在包含有Hll API 的应用中添加如下配置:

								-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
    }

							

8. 基础数据传输,计步,天气,语音识别,地理位置,请参考Fitwear API 部分相关代码。

9. 示例

关于Hll-api的详细用法可以参考项目SDK中的Demo。

基础数据传输

1. 开始使用

为了调试API,需要创建一个HilAplicent的实例,作为API的调用入口。

	HllApiClient mClient = new HllApiClient .Builder(this)
         .addConnectionCallbacks(new ConnectionCallbacks() {
              @Override
              public void onConnected(Bundle connectionHint) {
                   Log.d(TAG, "onConnected: " + connectionHint);
                   // Now you can use the Data Layer API
              }
              @Override
              public void onConnectionSuspended(int cause) {
                   Log.d(TAG, "onConnectionSuspended: " + cause);
              }
         })
         .addOnConnectionFailedListener(new OnConnectionFailedListener() {
              @Override
              public void onConnectionFailed(ConnectionResult result) {
                   Log.d(TAG, "onConnectionFailed: " + result);
         }
    })
    // Request access only to the Wearable API
    .addApi(Wearable.API)
    .build();

							

需要先调用connect(),等到onConnected()回调以后client才能正常使用。
注意: onConnected并不代表手表和手机之间已经建立起数据连接,只表示已经连接上Wearable API的服务,可以开始进行进程间调用。

2. 同步数据

一个DataItem定义了一个数据接口,该数据在手机端和手表端之间进行同步。一个DataItem通常包含以下内容:

* Payload: 一个字节数组。可以在里面放置任意数据,并采用自己的序列化和反序列化方法。payload的大小上限为100KB。

* Path: 一个唯一的字符串。必须以"/"开头,例如:/path/data。

通常并不需要自己实现DataItem,使用DataItem的步骤如下:

1) 创建一个PutDataRequest对象,定义一个唯一的路径作为标识。

2) 调用setData()来设置payload

3) 调用DataApi.putDataItem()来请求创建一个data item。

4) 当调用DataApi.getDataItem()请求一个data item时,会得到一个已经实现了对应接口的DataItem。

相比于直接使用setData()放置字节数组的方式,更建议使用data map,可以用类似Bundle的方式来操作data item,更加方便。使用DataMap的方法如下:

1) 创建一个PutDataMapRequest对象,设置data item的路径。该路径是一个唯一标识符,可以在手机端和手表端均使用该路径操作data item。

2) 调用PutDataMapRequest.getDataMap()获得一个data map,并可以往里面设值。

3) 使用put...()函数对data map进行设值。

4) 调用PutDataMapRequest.asPutDataRequest()生成一个PutDataRequest对象。

5) 调用DataApi.putDataItem()来创建一个data item。

监听data item事件:

如果连接的一方有数据的更改,那么这个更改会被通知到另一方。可以实现一个监听器来监听这些更改。

3. 传输asset

Asset相关的函数是一系列通过蓝牙连接发送大的二进制文件的方法。可以在data item中附带asset来发送图片之类的二进制文件。asset的机制自动处理了数据的缓存,以避免重新传输浪费蓝牙带宽。一个通用的用法是手机端应用下载一个图像,压缩到一个适合在手表上显示的尺寸,并把图像通过asset传输到手表端的app。

Data item的大小限制在100KB以下,asset的大小基本不受限制。可是,传输非常大的asset会影响用户体验,所以需要测试以确保不影响用户体验。

创建asset代码:

	private static Asset createAssetFromBitmap(Bitmap bitmap) { 
		final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
			bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteStream);      
				return Asset.createFromBytes(byteStream.toByteArray()); }

获得一个asset以后,可以使用DataMap或者PutDataRequest中的putAsset()方法将asset附加到一个data item上。

4. 发送和接收消息

可以通过MessageApi发送消息,一条消息包含以下内容:

    * Payload: 一个任意的字节数组。

    * Path: 一个唯一标识这条消息操作的字符串。

和DataItem不一样,消息在手机端和手表端没有同步。消息只是单向通信机制,比较适合远程调用。下面举例如何发送一条消息到手表端启动一个activity。

发送消息代码:

	HllApiClient mClient;
    public static final String START_ACTIVITY_PATH = "/start/MainActivity";
    ...
	 private void sendStartActivityMessage(String nodeId) {
        Wearable.MessageApi.sendMessage(
            mClient, nodeId, START_ACTIVITY_PATH, new byte[0]).setResultCallback(
                new ResultCallback() {
                    @Override
                    public void onResult(SendMessageResult sendMessageResult) {
                        if (!sendMessageResult.getStatus().isSuccess()) {
                            Log.e(TAG, "Failed to send message with status code: "
                                + sendMessageResult.getStatus().getStatusCode());
                        }
                    }
                }
        );
    }

							

接收消息代码:

	@Override
    public void onMessageReceived(MessageEvent messageEvent) {
         if (messageEvent.getPath().equals(START_ACTIVITY_PATH)) {
              Intent startIntent = new Intent(this, MainActivity.class);
              startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
              startActivity(startIntent);
         }
    }

							

5. WearableListenerService

继承并实现WearableListenerService的各个接口,来保证程序运行在后台时仍然能够接收message、data和node的事件。

继承的Service需要在AndroidManifest.xml里面进行声明,需要注意的是,Service不能声明exported为false。例如为DataLayerService,则添加声明如下:

	<service android:name=".DataLayerListenerService">
          	<intent-filter>
                 	<action android:name="com.hll.android.wearable.BIND_LISTENER" />
           	</intent-filter>
    	</service>

步数API

使用步数api之前要先注册步数服务类,可以在activity初始化时候注册 代码如下:

FitWearServiceManager.registerStepService(MainActivity.this);

在activity销毁时候注销服务代码如下:

FitWearServiceManager.unregisterStepService(MainActivity.this);

获取当前步数的代码如下:

int currentStep = StepUtils.getCurrentStep(MainActivity.this);

天气API

WeatherInfo包含以下字段 city, currentTemp, date, maxTemp, minTemp, tips, weather, weatherImg, week.

获取当前天气代码如下:WeatherUtils.getWeatherInfo(Context context, Date date, WeatherInfoListener listener);

WeatherUtils.getWeatherInfo(Context context, String cityName, Date date, WeatherInfoListener listener);

如果不写cityName就是查询当前位置的天气。

语音识别

1. 简介

在fitwear系统中提供语音查询语音输入两种语音识别API,语音识别准确率超过95%,实现快速精准的语音听写,语音输入速度达180字/分,识别结果响应时间低于500ms。语音查询API提供日常信息查询的转文字功能,而语音输入API则提供类似输入法的通用的语音输入功能。

2. 语音查询API

您只需要编写一个Activity,继承SpeechRecognitionApi.SpeechRecogActivity这个抽象类,并在Activity里面调用startRecognition()来发起语音识别。然后根据提示,完成两个回调函数onRecognitionSuccess(String result)和onRecognitionFailed(),用以接受语音识别结果。

如下示例代码:

	public class SpeechRecogTestActivity extends
        SpeechRecognitionApi.SpeechRecogActivity {
      private static final String TAG = "SpeechRecogTest";
      private TextView mTextView;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_speech_recog);
      }

      public void btnOnClick(View view) {
        // 按下按钮,启动语音识别
        startRecognition();
      }

      @Override
      public void onRecognitionSuccess(String speechRslt) {
        // 获得语音识别结果
        Log.d(TAG, "Get speech recognition result: " +
            speechRslt);
        TextView txtRslt = (TextView)
            findViewById(R.id.txtMain);
        txtRslt.setText(speechRslt);
      }

      @Override
      public void onRecognitionFailed() {
        // 当调用语音识别失败
        Log.e(TAG, "Speech recognition failed");
      }
    }
							

3. 语音输入API

您只需要编写一个Activity,继承SpeechRecognitionApi.SpeechRecogActivity这个抽象类,并在Activity里面调用startVoiceInput()来发起语音识别。然后根据提示,完成两个回调函数onRecognitionSuccess(String result)和onRecognitionFailed(),用以接受语音识别结果。

如下示例代码:

	package com.example.fitwear.voice;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
import com.hll.android.speech.SpeechRecognitionApi;
public class VoiceInputDemoActivity  extends SpeechRecognitionApi.SpeechRecogActivity {
    private static final String TAG = "VoiceInputDemoActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.dication_demo_activity);
        findViewById(R.id.bt_begin).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                startVoiceInput();
            }
        });
    }
    @Override
    public void onRecognitionSuccess(String text) {
        TextView txtRslt = (TextView) findViewById(R.id.speak_tip);
        txtRslt.setText(text);
    }
    @Override
    public void onRecognitionFailed() {
        TextView txtRslt = (TextView) findViewById(R.id.speak_tip);
        txtRslt.setText("onRecognitionFailed");
    }
}

							

地理位置

智能手表在没有GPS定位的情况下,fitwear提供了地理位置API方便开发者快速地获取当前用户的地理位置并进行应用开发。

为了调用API,需要先创建一个HllApiClient的实例,作为API的调用入口。

	public class MainActivity extends Activity implements
        HllApiClient.ConnectionCallbacks, HllApiClient.OnConnectionFailedListener{
    private static final String TAG = "MainActivity";
    private HllApiClient mClient;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }
    private void init() {
        mClient = new HllApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addApi(LocationServices.API)
                .build();
        mClient.connect();
    }
    @Override
    public void onConnected(Bundle bundle) {
        Log.i(TAG, "onConnected: ");
        Location mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mClient);
        if (mLastLocation != null) {
            TextView tv_location = (TextView) findViewById(R.id.tv_location);
            tv_location.setText(String.valueOf(mLastLocation.getLatitude()) + " " + String.valueOf(mLastLocation.getLongitude()));
        }
    }
    @Override
    public void onConnectionSuspended(int i) {
    }
    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
    }
}
							
							

getLastLocation()方法返回一个Location对象,从中可以获取到地理位置的经纬度,经纬度的坐标体系为原始GPS坐标。当地理位置不可获取时,location对象会为一个经纬度均为0的无效坐标值。