Google Fit — это платформа, которая позволяет разработчикам создавать приложения, ориентированные на данные о пригодности пользователей. Одним из инструментов, предоставляемых Google, является Google Fit для Android, который доступен в виде пакета в Сервисах Google Play.
В предыдущем уроке мы изучали, как использовать Google Fit Recording API для хранения данных о фитнесе через Сервисы Google Play. В этом учебном пособии эта тема раскрывается путем изучения того, как получать доступ к данным, хранящимся в Google Fit, и обновлять их с помощью API History.
Что делает Google Fit мощным, так это то, что данные о фитнесе хранятся через облачные сервисы Google, что позволяет получать к ним доступ и анализировать их позднее. В то время как API датчиков и записи ориентированы на сбор данных о пригодности с устройства Android, API истории предназначен для того, чтобы предоставить разработчикам легкий доступ к этим сохраненным данным.
В этом руководстве вы познакомитесь с примером проекта, который демонстрирует, как работать с History API. Готовый продукт можно скачать с GitHub .
1. Настройка проекта
Шаг 1. Настройка консоли разработчика
Чтобы использовать Google Fit, вам нужно создать идентификатор клиента OAuth 2.0 и зарегистрировать свое приложение через Консоль разработчика Google. Подробное объяснение того, как настроить проект в консоли разработчика Google, можно найти в моем руководстве по API Google Fit Sensors .
Шаг 2. Создайте проект Android
Как только ваше приложение будет настроено для аутентификации в консоли разработчика Google, используйте имя пакета, которое вы зарегистрировали, чтобы создать новое приложение Android с минимальным SDK 9 и пустым Activity
.
После создания базового приложения для Android откройте build.gradle , включите Google Play Services в узле зависимостей и синхронизируйте свое приложение.
1
|
compile ‘com.google.android.gms:play-services-fitness:8.4.0’
|
Теперь вы сможете включать необходимые классы Google Play Services в свое приложение. Прежде чем мы углубимся в код Java для этого учебника, откройте файл activity_main.xml и изменим его так, чтобы он включал пять элементов Button
, которые мы будем использовать для демонстрации некоторых функций API истории.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
<?xml version=»1.0″ encoding=»utf-8″?>
<LinearLayout xmlns:android=»http://schemas.android.com/apk/res/android»
android:layout_width=»match_parent»
android:layout_height=»match_parent»
android:paddingBottom=»@dimen/activity_vertical_margin»
android:paddingLeft=»@dimen/activity_horizontal_margin»
android:paddingRight=»@dimen/activity_horizontal_margin»
android:paddingTop=»@dimen/activity_vertical_margin»
android:orientation=»vertical»>
<Button
android:id=»@+id/btn_view_week»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:text=»View this weeks steps» />
<Button
android:id=»@+id/btn_view_today»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:text=»View today’s steps»/>
<Button
android:id=»@+id/btn_add_steps»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:text=»Add step data for today» />
<Button
android:id=»@+id/btn_update_steps»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:text=»Update step data for yesterday» />
<Button
android:id=»@+id/btn_delete_steps»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:text=»Delete step data for yesterday» />
</LinearLayout>
|
Когда вы запускаете ваше приложение, пользовательский интерфейс должен выглядеть так:
Чтобы продолжить настройку проекта, откройте MainActivity.java и реализуйте следующие обратные вызовы и необходимые методы:
-
GoogleApiClient.ConnectionCallbacks
-
GoogleAPiClient.OnConnectionFailedListener
-
View.OnClickListener
Вам также необходимо добавить переменную-член для GoogleApiClient
и набор ссылок на ваши объекты Button
, как показано ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
public class MainActivity extends AppCompatActivity implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
View.OnClickListener {
private Button mButtonViewWeek;
private Button mButtonViewToday;
private Button mButtonAddSteps;
private Button mButtonUpdateSteps;
private Button mButtonDeleteSteps;
private GoogleApiClient mGoogleApiClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButtonViewWeek = (Button) findViewById(R.id.btn_view_week);
mButtonViewToday = (Button) findViewById(R.id.btn_view_today);
mButtonAddSteps = (Button) findViewById(R.id.btn_add_steps);
mButtonUpdateSteps = (Button) findViewById(R.id.btn_update_steps);
mButtonDeleteSteps = (Button) findViewById(R.id.btn_delete_steps);
mButtonViewWeek.setOnClickListener(this);
mButtonViewToday.setOnClickListener(this);
mButtonAddSteps.setOnClickListener(this);
mButtonUpdateSteps.setOnClickListener(this);
mButtonDeleteSteps.setOnClickListener(this);
}
@Override
public void onConnectionSuspended(int i) {
Log.e(«HistoryAPI», «onConnectionSuspended»);
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.e(«HistoryAPI», «onConnectionFailed»);
}
public void onConnected(@Nullable Bundle bundle) {
Log.e(«HistoryAPI», «onConnected»);
}
@Override
public void onClick(View v) {
}
}
|
Последнее, что вам нужно сделать, чтобы настроить пример проекта, это подключиться к сервисам Google Play. Это можно сделать в конце метода onCreate()
.
1
2
3
4
5
6
|
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Fitness.HISTORY_API)
.addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE))
.addConnectionCallbacks(this)
.enableAutoManage(this, 0, this)
.build();
|
С помощью приведенного выше кода мы подключаемся к сервисам Google Play и запрашиваем доступ к History API в Google Fit, а также разрешение на чтение и запись данных об активности. Добавляя свойство enableAutoManage
, Службы Google Play управляют подключением и отключением правильно.
2. Использование API Google Fit History
Используя History API, вы можете извлекать и объединять данные, вставлять значения, которые вы собрали в своем приложении, или удалять данные, которые могут быть сохранены. Важно отметить, что все операции по History API в этом руководстве должны выполняться вне основного потока. Другими словами, каждая операция выполняется в AsyncTask
:
1
2
3
4
5
6
|
private class ViewWeekStepCountTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void… params) {
displayLastWeeksData();
return null;
}
}
|
Шаг 1: Отображение данных с течением времени
Одна из задач, которую вы, возможно, захотите выполнить, — использовать или отображать данные, которые были собраны с течением времени. Вы можете сделать это, создав новый DataReadRequest
для определенного типа данных, который соответствует некоторым параметрам запроса.
Вы можете сгруппировать эти данные вместе в объектах Bucket
по времени или сеансам. В этом примере вы запрашиваете агрегированные данные шага за последнюю неделю и собираете эти данные по дням.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.WEEK_OF_YEAR, -1);
long startTime = cal.getTimeInMillis();
java.text.DateFormat dateFormat = DateFormat.getDateInstance();
Log.e(«History», «Range Start: » + dateFormat.format(startTime));
Log.e(«History», «Range End: » + dateFormat.format(endTime));
//Check how many steps were walked and recorded in the last 7 days
DataReadRequest readRequest = new DataReadRequest.Builder()
.aggregate(DataType.TYPE_STEP_COUNT_DELTA, DataType.AGGREGATE_STEP_COUNT_DELTA)
.bucketByTime(1, TimeUnit.DAYS)
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
.build();
|
GoogleApiClient
DataReadRequest
, вы можете запросить данные у GoogleApiClient
с помощью команды Fitness.HistoryApi.readData
. Вы также вызываете await
на API-клиенте, так что поток ожидает до минуты, ожидая запроса. Если что-то идет не так, поток продолжается после этой минуты.
1
|
DataReadResult dataReadResult = Fitness.HistoryApi.readData(mGoogleApiClient, readRequest).await(1, TimeUnit.MINUTES);
|
Если вызов успешно завершен, вы можете получить доступ к данным о пригодности пользователя из объекта DataReadResult
. Ваш запрос данных шага возвращается в объектах Bucket
, хотя другие типы данных возвращаются в виде List
объектов DataSet
. В зависимости от типа возвращаемых данных вы можете получить к ним следующий код:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
//Used for aggregated data
if (dataReadResult.getBuckets().size() > 0) {
Log.e(«History», «Number of buckets: » + dataReadResult.getBuckets().size());
for (Bucket bucket : dataReadResult.getBuckets()) {
List<DataSet> dataSets = bucket.getDataSets();
for (DataSet dataSet : dataSets) {
showDataSet(dataSet);
}
}
}
//Used for non-aggregated data
else if (dataReadResult.getDataSets().size() > 0) {
Log.e(«History», «Number of returned DataSets: » + dataReadResult.getDataSets().size());
for (DataSet dataSet : dataReadResult.getDataSets()) {
showDataSet(dataSet);
}
}
|
Затем вы можете извлечь каждый DataPoint
из объектов DataSet
, которые были возвращены.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
private void showDataSet(DataSet dataSet) {
Log.e(«History», «Data returned for Data type: » + dataSet.getDataType().getName());
DateFormat dateFormat = DateFormat.getDateInstance();
DateFormat timeFormat = DateFormat.getTimeInstance();
for (DataPoint dp : dataSet.getDataPoints()) {
Log.e(«History», «Data point:»);
Log.e(«History», «\tType: » + dp.getDataType().getName());
Log.e(«History», «\tStart: » + dateFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)) + » » + timeFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)));
Log.e(«History», «\tEnd: » + dateFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS)) + » » + timeFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)));
for(Field field : dp.getDataType().getFields()) {
Log.e(«History», «\tField: » + field.getName() +
» Value: » + dp.getValue(field));
}
}
}
|
Если приложение на устройстве пользователя включило API записи в течение последней недели, вы получите данные за каждый день, когда оно было активным. Журналы из приведенного выше кода должны выглядеть примерно так:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
E/History: Range Start: Feb 17, 2016
E/History: Range End: Feb 24, 2016
E/History: Number of buckets: 7
E/History: Data returned for Data type: com.google.step_count.delta
E/History: Data point:
E/History: Type: com.google.step_count.delta
E/History: Start: Feb 17, 2016 11:01:46 PM
E/History: End: Feb 17, 2016 11:01:46 PM
E/History: Field: steps Value: 9360
E/History: Data returned for Data type: com.google.step_count.delta
E/History: Data returned for Data type: com.google.step_count.delta
E/History: Data returned for Data type: com.google.step_count.delta
E/History: Data point:
E/History: Type: com.google.step_count.delta
E/History: Start: Feb 21, 2016 9:58:03 AM
E/History: End: Feb 21, 2016 9:58:03 AM
E/History: Field: steps Value: 10041
E/History: Data returned for Data type: com.google.step_count.delta
E/History: Data point:
E/History: Type: com.google.step_count.delta
E/History: Start: Feb 21, 2016 11:22:53 PM
E/History: End: Feb 22, 2016 11:22:53 PM
E/History: Field: steps Value: 10786
E/History: Data returned for Data type: com.google.step_count.delta
E/History: Data point:
E/History: Type: com.google.step_count.delta
E/History: Start: Feb 22, 2016 11:06:31 PM
E/History: End: Feb 23, 2016 11:06:31 PM
E/History: Field: steps Value: 13099
E/History: Data returned for Data type: com.google.step_count.delta
E/History: Data point:
E/History: Type: com.google.step_count.delta
E/History: Start: Feb 23, 2016 11:02:31 PM
E/History: End: Feb 24, 2016 11:02:31 PM
E/History: Field: steps Value: 9765
|
Шаг 2: Просмотр данных за сегодня
Теперь вы знаете, как получить данные Google Fit на основе таймфрейма. Один из наиболее распространенных вариантов использования — это данные за текущий день. К счастью, Google сделал это проще, добавив вызов Fitness.HistoryApi.readDailyTotal
, который создает объект DailyTotalResult
содержащий данные, собранные за день.
1
2
3
4
|
private void displayStepDataForToday() {
DailyTotalResult result = Fitness.HistoryApi.readDailyTotal( mGoogleApiClient, DataType.TYPE_STEP_COUNT_DELTA ).await(1, TimeUnit.MINUTES);
showDataSet(result.getTotal());
}
|
Приведенный выше код записывает данные о шагах пользователя за день.
1
2
3
4
5
6
|
E/History: Data returned for Data type: com.google.step_count.delta
E/History: Data point:
E/History: Type: com.google.step_count.delta
E/History: Start: Feb 24, 2016 8:39:59 AM
E/History: End: Feb 24, 2016 8:39:59 AM
E/History: Field: steps Value: 9474
|
Шаг 3: Вставка данных в Google Fit
Хотя возможность считывания данных из Google Fit важна, в некоторых случаях вам может понадобиться добавить свои собранные данные в хранилище данных Google Fit. Для этого вы выбираете временной диапазон для данных и создаете объект DataSource
который представляет информацию, которую вы помещаете в Google Fit. Затем вы создаете объект DataSet
и помещаете в него новый DataPoint
для операции вставки.
В этом примере мы предположим, что пользователь прошел один миллион шагов, или примерно 500 миль, и вставим эти необработанные данные о шагах в последний час активности пользователя.
Важно отметить, что если вы выберете временной диапазон, охватывающий несколько дней, количество вставляемых шагов будет равномерно распределено между этими днями.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.HOUR_OF_DAY, -1);
long startTime = cal.getTimeInMillis();
DataSource dataSource = new DataSource.Builder()
.setAppPackageName(this)
.setDataType(DataType.TYPE_STEP_COUNT_DELTA)
.setName(«Step Count»)
.setType(DataSource.TYPE_RAW)
.build();
int stepCountDelta = 1000000;
DataSet dataSet = DataSet.create(dataSource);
DataPoint point = dataSet.createDataPoint()
.setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS);
point.getValue(Field.FIELD_STEPS).setInt(stepCountDelta);
dataSet.add(point);
|
После создания объекта DataSet
его можно вставить в хранилище данных Google Fit с помощью вызова insertData
API insertData
.
1
|
Fitness.HistoryApi.insertData(mGoogleApiClient, dataSet).await(1, TimeUnit.MINUTES);
|
Если вы вставите шаги пользователя, а затем прочитаете журнал активности, вы должны увидеть что-то похожее на следующее:
1
2
3
4
5
6
|
E/History: Data returned for Data type: com.google.step_count.delta
E/History: Data point:
E/History: Type: com.google.step_count.delta
E/History: Start: Feb 27, 2016 10:05:18 PM
E/History: End: Feb 28, 2016 10:05:18 PM
E/History: Field: steps Value: 1008747
|
Шаг 4. Обновление данных в Google Fit
После того, как вы вставили данные в Google Fit, вы больше не сможете вставить данные такого типа, если они перекрываются с существующими данными. Это может вызвать проблемы, если пользователь прошел 500 миль, а затем прошел еще 500.
Чтобы решить эту проблему, вам нужно использовать вызов updateData
. К счастью, обновление данных практически идентично вставке новых данных, за исключением того, что вместо этого вы создаете DataUpdateRequest
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.HOUR_OF_DAY, -1);
long startTime = cal.getTimeInMillis();
DataSource dataSource = new DataSource.Builder()
.setAppPackageName(this)
.setDataType(DataType.TYPE_STEP_COUNT_DELTA)
.setName(«Step Count»)
.setType(DataSource.TYPE_RAW)
.build();
int stepCountDelta = 2000000;
DataSet dataSet = DataSet.create(dataSource);
DataPoint point = dataSet.createDataPoint()
.setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS);
point.getValue(Field.FIELD_STEPS).setInt(stepCountDelta);
dataSet.add(point);
DataUpdateRequest updateRequest = new DataUpdateRequest.Builder().setDataSet(dataSet).setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS).build();
Fitness.HistoryApi.updateData(mGoogleApiClient, updateRequest).await(1, TimeUnit.MINUTES);
|
Если вы должны были выйти из системы на следующий день после выполнения вышеизложенного, вы должны увидеть более двух миллионов шагов.
1
2
3
4
5
6
|
E/History: Data returned for Data type: com.google.step_count.delta
E/History: Data point:
E/History: Type: com.google.step_count.delta
E/History: Start: Feb 27, 2016 10:05:18 PM
E/History: End: Feb 28, 2016 10:05:18 PM
E/History: Field: steps Value: 2008747
|
Шаг 5. Удаление данных из Google Fit
Последней операцией, доступной через API истории Google Fit, является возможность удаления данных, которые вы сохранили в Google Fit. Это можно сделать, выбрав диапазон дат, создав объект DataDeleteRequest
и вызвав deleteData
из History API. Хотя вы можете удалить свои собственные вставленные данные, данные, сохраненные в Google Fit, не удаляются.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.DAY_OF_YEAR, -1);
long startTime = cal.getTimeInMillis();
DataDeleteRequest request = new DataDeleteRequest.Builder()
.setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
.addDataType(DataType.TYPE_STEP_COUNT_DELTA)
.build();
Fitness.HistoryApi.deleteData(mGoogleApiClient, request).await(1, TimeUnit.MINUTES);
|
Вывод
Как вы уже видели, History API — это невероятно мощный инструмент для доступа к хранилищу данных Google Fit и управления им. В то время как другие API Google Fit предназначены для сбора данных, API истории позволяет поддерживать и получать доступ к данным для анализа. Теперь вы сможете создавать удивительные приложения, использующие эту функцию.