google's beacon platform. Частина 2 — Nearby meassages API

google's beacon platform — це рішення для роботи з Bluetooth маячками. Платформа працює з різними маячками від різних виробників, надаючи розробникам єдиний, простий і гнучкий інструмент.


Перед прочитанням цієї статті я рекомендую ознайомитися з концепцією Physical Web про яку я розповідав у своїй минулій статті: Концепція Physical web. Bluetooth маячки. Порівняння стандартів iBeacon, AltBeacon і Eddystone.

google's beacon platform. Частина 1 — Proximity beacon API
google's beacon platform. Частина 2 — Nearby meassages API

Основним засобом, в рамках google's beacon platform, для роботи на клієнтській стороні з bluetooth маячками, є Nearby Messages API. У цій статті я розповім як налаштувати проекти а на платформах Android і iOS, і додати в додаток можливість отримувати і розглядати повідомлення від ble-маячків.

Nearby Messages API
Nearby Messages API — це API, яке реалізує парадигму publish-subscribe і дозволяє різним пристроям публікувати і підписуватися на повідомлення, таким чином обмінюватися даними. Nearby Messages API є частиною Nearby. Для обміну повідомленнями пристрою не обов'язково повинні знаходитися в одній мережі, але повинні бути підключені до інтернету. У нашому випадку підключення до інтернету має бути у того смартфона або планшета на якому ми хочемо отримувати повідомлення. Маячками підключення до інтернету не потрібно! Nearby Messages API дозволяє обмінюватися повідомленнями за допомогою Bluetooth, Bluetooth Low Energy, Wi-Fi і навіть ультразвуку, але ми будемо використовувати тільки Bluetooth Low Energy що б мінімізувати споживання енергії.

Nearby Messages API на Android
Nearby Messages API доступний на пристроях з Android в бібліотеці Google Play services версії 7.8.0 або вище.
Убедитесть що у вас встановлена остання версія клієнтської бібліотеки для Google Play на вашому хості для розробки:

  • Відкрийте Android SDK Manager.
  • Перейдіть в A ppearance & Behavior > System Settings > Android SDK > SDK Tools і переконайтеся, що встановлені наступні пакети:
    • Google Play services
    • Google Repository

Для використання Nearby Messages API, звичайно ж знадобиться Google Account. Так само необхідно отримати API ключ. Ключі для Android, iOS і Proximity Beacon API повинні бути створені в рамках одне проекту Google Developers Console. Дуже раджу ознайомитися з Best practices for securely using API keys

Обережно GIF. Приклад того, як отримати API ключ:image


Налаштовуємо проект:
Відкриваємо або створюємо новий проект, відкриваємо
build.gradle
файл і додаємо в нього Google Play services client library як залежність.

Зміст файлу build.gradle
apply plugin: 'android'
...

dependencies {
compile 'com.google.android.gms:play-services-nearby:8.4.0'
}


Налаштовуємо manifest файл в який додаємо згенерований раніше API ключ:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.sample.app" >
<application ...>
<meta-data
android:name="com.google.android.nearby.messages.API_KEY"
android:value="API_KEY" />
<activity>
...
</activity>
</application>
</manifest>


Створюємо в нашому додатку GoogleApiClient і додаємо Nearby Messages API
Приклад коду який показує як додати Nearby Messages API:

Створюємо в додатку GoogleApiClient:
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Nearby.MESSAGES_API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();


Отримання повідомлень
Для отримання повідомлень від маячків нам необхідно спершу на них підписатися, є два способи як наш додаток може це зробити:

  • В активному режимі додатка, у відповідь на дії користувача або події.
  • У фоновому режимі, тобто коли додаток неактивно
Nearby Messages API вимагає дозволу на запити
publish()
та
subscribe()
. Тому програма повинна перевіряти на що користувач вже дав свою згоду і якщо така відсутня, то викликати діалог запиту дозволів.

Що б реалізувати запит дозволу у користувача під час виконання вашої програми, ви можете:

  • Приєднати
    result callback
    до викликів
    publish()
    and
    subscribe()
    .
  • Nearby.Messages.getPermissionStatus()
    що б перевірити статус дозволу безпосередньо перед викликом
    publish()
    або
    subscribe()
Реалізація result callback
Для перевірки статус коду помилки в
result callback
необхідно викликати
status.getStatusCode()
. Якщо отриманий статус код
APP_NOT_OPTED_IN
відобразіть діалог запиту дозволів викликом
status.startResolutionForResult()
і використовувати
onActivityResult()
що б повторно реинициировать які-небудь невиконані запити підписки або публікації.

Наступний приклад демонструє простий result callback, який перевіряє, які користувач надав дозволу. Якщо користувач не надав дозволів, викликається
status.startResolutionForResult()
, щоб запропонувати користувачеві дозволити Nearby Messages. У цьому прикладі
boolean mResolvingError
використовується щоб уникнути багаторазового запуску таких пропозицій:

Приклад реалізації result callback:
private void handleUnsuccessfulNearbyResult(Status status) {
Log.i(TAG, "error Processing, status = " + status);
if (mResolvingError) {
// Already attempting to resolve an error.
return;
} else if (status.hasResolution()) {
try {
mResolvingError = true;
status.startResolutionForResult(getActivity(),
Constants.REQUEST_RESOLVE_ERROR);
} catch (IntentSender.SendIntentException e) {
mResolvingError = false;
Log.i(TAG, "Failed to resolve error status.", e);
}
} else {
if (status.getStatusCode() == CommonStatusCodes.NETWORK_ERROR) {
Toast.makeText(getActivity().getApplicationContext(),
"No connectivity, cannot proceed. Fix in 'Settings' and try again.",
Toast.LENGTH_LONG).show();
} else {
// To keep simple things, pop a toast for all other error messages.
Toast.makeText(getActivity().getApplicationContext(), "Unsuccessful: " +
status.getStatusMessage(), Toast.LENGTH_LONG).show();
}
}
}


Ви також можете використовувати цей обробник(handler) для обробки будь-яких інших
NearbyMessageStatusCodes
отриманих від операцій або, наприклад, статус код
CommonStatusCodes.NETWORK_ERROR.


Реиницализация невиконаних запитів

Наступний приклад показує реалізацію методу
onActivityResult()
, який викликається після того, як користувач відреагує на діалог запиту дозволів. У разі якщо користувач відповість згодою, будь-які очікують запити підписки або публікації будуть виконані. А даному прикладі викликається метод
executePendingTasks()


Реиницаиалзация невиконаних запитів:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (requestCode == Constants.REQUEST_RESOLVE_ERROR) {
// User was presented with the Nearby opt-in dialog and pressed "Allow".
mResolvingError = false;
if (resultCode == Activity.RESULT_OK) {
// Execute the pending subscription and publication tasks here.
mMainFragment.executePendingTasks();
} else if (resultCode == Activity.RESULT_CANCELED) {
// User declined to opt-in. Reset application state here.
} else {
Toast.makeText(this, "Failed to resolve error with code " + resultCode,
Toast.LENGTH_LONG).show();
}
}


Так само, щоб зменшити затримку при скануванні маячків, рекомендується використовувати Strategy.BLE_ONLY при виклику Nearby.Messages.subscribe(). Коли ця опція не встановлена, Nearby Messages API не задіює Wi-Fi сканування або класичні сканування Bluetooth. Це зменшує затримку виявлення маячків, оскільки система не циклирует через всі можливі типи сканування, а так само зменшує споживання енергії.

Підписка в активному режимі:
Коли ваш додаток підписується на повідомлення від маячків будучи в активному режимі, сканування проводиться безперервно поки додаток не відпише. Таку підписку рекомендується використовувати тільки коли ваш додаток активно, зазвичай у відповідь на якісь дії користувача.

Додаток може ініціювати підписку в активному режимі викликавши метод
Nearby.Messages.subscribe(GoogleApiClient, MessageListener, SubscribeOptions)
та виберіть для параметра
Strategy
значення
BLE_ONLY
.

Фрагмент коду демонструє ініціювання підписки у активному режимі:
// Create a new message listener.
mMessageListener = new MessageListener() {
@Override
public void onFound(Message message) {
// Do something with the message.
Log.i(TAG, "Found message: " + message);
}

// Called when a message is no longer detectable nearby.
public void onLost(Message message) {
// Take appropriate action here (update UI, etc.)
}
}

// Subscribe to receive messages.
Log.i(TAG, "Trying to subscribe.");
// Connect the GoogleApiClient.
if (!mGoogleApiClient.isConnected()) {
if (!mGoogleApiClient.isConnecting()) {
mGoogleApiClient.connect();
}
} else {
SubscribeOptions options = new SubscribeOptions.Builder()
.setStrategy(Strategy.BLE_ONLY)
.setCallback(new SubscribeCallback() {
@Override
public void onExpired() {
Log.i(TAG, "No longer subscribing.");
}
}).build();

Nearby.Messages.subscribe(mGoogleApiClient, mMessageListener, options)
.setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(Status status) {
if (status.isSuccess()) {
Log.i(TAG, "Subscribed successfully.");
} else {
Log.i(TAG, "Could not subscribe.");
// Check whether consent was given;
// if not, prompt the user for consent.
handleUnsuccessfulNearbyResult(status);
}
}
});
}


Для зменшення споживання енергії і, відповідно, продовження терміну роботи акумулятора, викликайте
Nearby.Messages.unsubscribe() в методі
OnStop()
вашого додатка. Коли підписка на повідомлення більше не потрібна, додаток має відписатися викликавши метод
Nearby.Messages.unsubscribe(GoogleApiClient, MessageListener)`.

Передплата у фоновому режимі
Коли додаток підписується на повідомлення у фоновому режимі, спрацьовує low-power сканування при подіях включення екрану, навіть коли додаток в даний час не активно. Ви можете використовувати ці фонові події low-power сканування, щоб "розбудити" додаток у відповідь на конкретне повідомлення. Фонові підписки споживають менше енергії, ніж передплата в активному режимі, але мають більш високу затримку і низьку надійність.

Підписка у фоновому режимі ініціюється викликом методу
Nearby.Messages.subscribe(GoogleApiClient, PendingIntent,SubscribeOptions)
і завданням для параметра
Strategy
значення
BLE_ONLY
.

Фрагмент коду демонструє ініціювання підписки у фоновому режимі:
// Subscribe to messages in the background.
private void backgroundSubscribe() {
// Connect the GoogleApiClient.
if (!mGoogleApiClient.isConnected()) {
if (!mGoogleApiClient.isConnecting()) {
mGoogleApiClient.connect();
}
} else {
Log.i(TAG, "Subscribing background for updates.");
SubscribeOptions options = new SubscribeOptions.Builder()
.setStrategy(Strategy.BLE_ONLY)
.build();
Nearby.Messages.subscribe(mGoogleApiClient, getPendingIntent(), options)
.setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(Status status) {
if (status.isSuccess()) {
Log.i(TAG, "Subscribed successfully.");
} else {
Log.i(TAG, "Could not subscribe.");
handleUnsuccessfulNearbyResult(status);
}
}
});
}
}

private PendingIntent getPendingIntent() {
return PendingIntent.getService(getApplicationContext(), 0,
getBackgroundSubscribeServiceIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
}

private Intent getBackgroundSubscribeServiceIntent() {
return new Intent(getApplicationContext(), BackgroundSubscribeIntentService.class);
}


Фрагмент коду демонструє обробку intent'а в класі BackgroundSubscribeIntentService:
protected void onHandleIntent(Intent intent) {
Nearby.Messages.handleIntent(intent, new MessageListener() {
@Override
public void onFound(Message message) {
Log.i(TAG, "Found message via PendingIntent: " + message);
}

@Override
public void onLost(Message message) {
Log.i(TAG, "Lost message via PendingIntent: " + message);
}
});
}


Ну і звичайно ж, якщо підписка нам більше не потрібна, ми повинні відписатися викликавши
Nearby.Messages.unsubscribe(GoogleApiClient, PendingIntent)
.

Розбір повідомлень
Як ми вже знаємо з першої частини, кожне вкладення складається з наступних частин:
-Namespace: Ідентифікатор простору імен.
-Type: Тип даних.
-Data: Значення даних вкладення.

Фрагмент коду демонструє використання MessageListener() для розбору повідомлень:
mMessageListener = new MessageListener() {
@Override
public void onFound(Message message) {
// Do something with the message here.
Log.i(TAG, "Message found: " + message);
Log.i(TAG, "Message string: " + new String(message.getContent()));
Log.i(TAG, "Message namespaced type: " + message.getNamespace() +
"/" + message.getType());
}

...
};


Варто врахувати, що розбір змісту залежить від формату байт. Цей приклад припускає, що повідомлення закодовані в UTF-8 рядок, але ваші повідомлення від маячків можуть бути закодовані в інший формат.
Щоб дізнатися, які простору імен пов'язані з вашим проектом, можна викликати namespaces.list.

Nearby Nearby meassages API на iOS
Для створення проекту використовує Nearby Messages API for iOS нам знадобитися Xcode версії 6.3 або старше.

Google Nearby Messages API для iOS доступний пакет(pod) CocoaPods. CocoaPods — це менеджер залежностей з відкритим вихідним кодом для Swift і Objective-C, Cocoa проектів. Для отримання додаткової інформації раджу ознайомитися з CocoaPods Getting Started guide. Якщо він у вас не встановлений, ви можете зробити це виконавши в терміналі наступну комманду:

$ sudo gem install cocoapods

Встановлюємо Nearby messages API використовую CocoaPods:

  • Відкриваємо проект або створюємо новий, слід переконатися, що опція Use Automatic Reference Counting включена.
  • Створюємо файл з ім'ям Podfile в директорії проекту. Цей файл буде визначати залежно проекту.
  • Додаємо в залежності Podfile. Приклад простого Podspec містить ім'я пакета яке для установки
    source 'https://github.com/CocoaPods/Specs.git'
    platform :ios, '7.0'
    pod 'NearbyMessages'
  • В терміналі переходимо в директорю у якій знаходиться Podfile
  • Для установки разом з залежності API зазначених у Podfile необхідно виконати комманду:
    $ pod install
Після цього закриваємо Xcode і потім подвійним кліком по .xcworkspace проекту запускаємо Xcode. Починаючи з цього моменту, ви повинні використовувати файл .xcworkspace для відкриття проекту.

Як і у випадку з Android, нам необхідно отримати API ключ. Ключі для Android, iOS і Proximity Beacon API повинні бути створені в рамках одне проекту Google Developers Console. Дуже раджу ознайомитися з Best practices for securely using API keys

Обережно GIF. Приклад отримання API ключа:image


Тепер, коли все налаштовано, ми можемо створити об'єкт
messageManager
і використовувати API ключ створений раніше

#import <GNSMessages.h>

GNSMessageManager *messageManager =
[[GNSMessageManager alloc] initWithAPIKey:@"API_KEY"];

Підписка в iOS
У iOS сканування маячків відбувається тільки коли програма активна. Сканування маячків у фоновому режимі недоступне для iOS. Що б підписатися тільки на BLE маячки потрібно задати
deviceTypesToDiscover
в параметрах підписки
kGNSDeviceBLEBeacon
.

Фрагмент коду, який показує, як це зробити:
_beaconSubscription = [_messageManager
subscriptionWithMessageFoundHandler:myMessageFoundHandler
messageLostHandler:myMessageLostHandler
paramsBlock:^(GNSSubscriptionParams *params) {
params.deviceTypesToDiscover = kGNSDeviceBLEBeacon;
}];


Цей приклад коду підписується тільки на маячки нашого проекту і отримує всі повідомлення від них.

Якщо ми хочемо отримувати повідомлення від маячків зареєстрованих з іншим простором імен, які ми можемо передати namespace параметри підписки. Аналогічно ми можемо зробити і з типом повідомлень які хочемо отримувати передавши конкретний тип повідомлень для фільтрації. Для цього необхідно задіяти сканування пристроїв
GNSStrategy
і прокинути значення простору імен і типу підписки параметри підписки.

Фрагмент коду, який показує, як це зробити:
_beaconSubscription = [_messageManager
subscriptionWithMessageFoundHandler:myMessageFoundHandler
messageLostHandler:myMessageLostHandler
paramsBlock:^(GNSSubscriptionParams *params) {
params.deviceTypesToDiscover = kGNSDeviceBLEBeacon;
params.messageNamespace = @"com.mycompany.mybeaconservice";
params.type = @"mybeacontype";
}];


За замовчуванням, при підписці ми скануємо відразу обидва типи маячків, Eddystone і iBeacon. Якщо у нас задіяно сканування маячків iBeacon, користувач отримає запит на використання геолокації. Info.plist програми повинен включати ключ
NSLocationAlwaysUsageDescription
з крткаим обьяснением того, чому используеься геолокація. Раджу ознайомитися з документацією Apple для деталей.

Якщо ми хочемо сканувати тільки маячки Eddystone, ми можемо відключити сканування маячків iBeacon
GNSBeaconStrategy
. У такому разі iOS не буде запитувати у користувача дозволу на використання геолокації.

Фрагмент коду, який показує як відключити сканування iBeacon маячків для попереднього прикладу:
_beaconSubscription = [_messageManager
subscriptionWithMessageFoundHandler:myMessageFoundHandler
messageLostHandler:myMessageLostHandler
paramsBlock:^(GNSSubscriptionParams *params) {
params.deviceTypesToDiscover = kGNSDeviceBLEBeacon;
params.messageNamespace = @"com.mycompany.mybeaconservice";
params.type = @"mybeacontype";
params.beaconStrategy = [GNSBeaconStrategy strategyWithParamsBlock:^(GNSBeaconStrategyParams *params) {
params.includeIBeacons = NO;
};
}];


При скануванні маячків iBeacon, діалог запиту дозволів на геолокацію передує діалогу дозволів Nearby. Ми можемо перевизначити цей діалог, наприклад, для того що б обяьснить користувачеві для чого запитується дозвіл на геолокацію. Для цього необхідно задати
permissionRequestHandler
в окремому блоці параметри підписки.

Фрагмент коду показує як це зробити:
_beaconSubscription = [_messageManager
subscriptionWithMessageFoundHandler:myMessageFoundHandler
messageLostHandler:myMessageLostHandler
paramsBlock:^(GNSSubscriptionParams *params) {
params.deviceTypesToDiscover = kGNSDeviceBLEBeacon;
params.messageNamespace = @"com.mycompany.mybeaconservice";
params.type = @"mybeacontype";
params.beaconStrategy = [GNSBeaconStrategy strategyWithParamsBlock:^(GNSBeaconStrategyParams *params) {
params.includeIBeacons = NO;
};
params.permissionRequestHandler = ^(GNSPermissionHandler permissionHandler) {
// Show your custom dialog here, and don't forget to call permissionHandler after it is dismissed
permissionHandler(userGavePermission);
};
}];


Висновок
Я сподіваюсь, що даний матеріал буде комусь корисний, зекономить час і сили.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.