GPS нынче – одна из часто востребованных функций в мобильных устройствах и поэтому в этот раз будем рассматривать вопросы использования GPS в приложениях для Palm WebOS.

Итак… Работа с GPS в WebOS осуществляется посредством вызова системного сервиса palm://com.palm.location. Вызов сревиса происходит с помощью метода serviceRequest() контроллера приложения. Тоесть в коде это выглядит как-то так:

this.controller.serviceRequest('palm://com.palm.location', ...);

В WebOS сервис, осуществляющий взаимодействие с GPS-приемником имеет три публичных метода:

  • getCurrentPosition – Синхронный метод получения актуальных GPS-координат
  • startTracking – Подписка на изменение GPS-координат
  • getReverseLocation – Получение информации по указанным координатам (геокодер)

Рассмотрим каждый метод по очереди.

getCurrentPosition

В документации в качестве примера использования этого метода значится вот такой кусок кода:

this.controller.serviceRequest('palm://com.palm.location', {
   method:"getCurrentPosition",
   parameters:{},
   onSuccess:{},
   onFailure:{}
   }
});

На самом же деле этот кусок кода описывает вызов метода только формально и вводит в заблуждение. Первое, с чем приходится столкнутьс, это доступ к полученным координатам. Если писать что-то внутри операторных скобок, которые указаны в качестве значения параметра onSuccess, то получим ошибку. Правильным же способом является указание метода-обработчика для onSuccess и onFailure.

  • onSuccess – метод, который будет вызван в случае если запрос отработал успешно
  • onFailure – метод, который будет вызван в случае если запрос завершился с ошибкой

Тоесть минимально рабочим примером, который позволит нам получить координаты будет следующий код:

FirstAssistant.prototype.handleCoordButton = function(event)
{
	this.controller.serviceRequest(
		'palm://com.palm.location', 
		{
   			method:"getCurrentPosition",
   			parameters: {},
   			onSuccess: this.getCooSuccessResponseHandler.bind(this),
   			onFailure: {}
   		});
}

FirstAssistant.prototype.getCooSuccessResponseHandler = function(event){
	this.controller.showAlertDialog({
    title: $L("Location"),
    message: 'Latitude: '+event.latitude+'\nLongitude: '+event.longitude,
    choices:
    	[
			{label:$L("OK"), value:"ok"}    
    	]
    });
}

Как видно в примере, мы указываем обработчик только для onSuccess. Для onFailure это делается аналогичным образом. В обработчике onFailure модно также узнать код ошибки. Код ошибки содержится в свойстве errorCode объекта, передаваемого в качестве параметра обработчика. errorCode – это целочисленное свойство, которое может принимать одно из следующих значений:

  • 0: Нет ошибки
  • 1: Таймаут
  • 2: Координаты недоступны
  • 3: Неизвестная ошибка
  • 5: Выключен сервис GPS
  • 6: Доступ запрещен – Пользователь не принял условия лицензионного соглашения на использование сервиса GPS.
  • 7: У приложения уже есть сообщение в очереди
  • 8: Приложение временно в черном списке.

Если вы тестируете свое приложение на эмуляторе, то скорее всего сразу получите ошибку с кодом 6. Для того чтобы это исправить, надо в Launcher’е выбрать пункт Location Services и там согласиться с условиями лицензионного соглашения на использование геолокационных сервисов.
Кроме, собственно, широты и долготы, в обработчике получения координат можно узнать еще кучу полезной информации. Более детально о списке доступны параметров можно узнать здесь.

startTracking

Подписка на получение координат от сервиса. Очень полезная штука. Позволяет в асинхронном режиме получать новые значения координат. Пример из документации опять говорит нам вот что:

this.controller.serviceRequest('palm://com.palm.location', {
   method:"startTracking",
   parameters:{},
   onSuccess:{},
   onFailure:{}
   }
});

И здесь, опять же, снова нехватает нормальной демонстрации того, как надо указывать обработчики для onSuccess и onFailure.
А на самом деле все должно быть как-то так:

first-scene.html

<div id="main" class="palm-hasheader">
     <div class="palm-header">GPS Test</div>
     <div id="coordinates" class="palm-body-text">0</div>
</div>

first-assistant.js

...
FirstAssistant.prototype.setup = function() {
     this.controller.get("coordinates").update('Unknown');
     
     this.points = 0;
     this.trackingHandle = this.controller.serviceRequest(
     	'palm://com.palm.location', 
     	{
			method:"startTracking",
			parameters: {"subscribe":true},
			onSuccess:  this.trackingSuccessResponseHandler.bind(this),
			onFailure:  this.trackingFailedResponseHandler.bind(this)
		});
}

FirstAssistant.prototype.cleanup = function(event) {
	this.trackingHandle.cancel();
}
...
FirstAssistant.prototype.trackingSuccessResponseHandler = function(event){
	this.points++;
	this.controller.get("coordinates").update(
		'Coordinates:<ul>'+ 
		'<li>Count: ' + this.points + '</li>'+
		'<li>Latitude: ' + event.latitude + '</li>'+
		'<li>Longitude: ' + event.longitude+'</li>'+
		'</ul>');
}

FirstAssistant.prototype.trackingFailedResponseHandler = function(event){
	this.controller.get("coordinates").update(
		'Failed to retrieve coordinates: ' + event.errorCode);
}
...

Что мы тут сделали:

  • Создали текстовое поле с идентификатором coordinates
  • В конструкторе подписались на событие получения GPS-координат
  • Когда координаты приходят, показываем их в текстовом поле вместе со счетчиком получений
  • Если происходит ошибка, то показываем сообщение об ошибке и код ошибки
  • В деструкторе отписываемся от события вызвав метод this.trackingHandle.cancel()

Обо всех остальных параметрах, получаемых в onSuccess можно прочитать здесь.

getReverseLocation

Пример кода в документации следующий:

this.controller.serviceRequest('palm://com.palm.location', {
   method:"getReverseLocation",
   parameters:{},
   onSuccess:{},
   onFailure:{}
   }
});

В качестве параметров метода могут фигурировать latitude и longitude – широта и долгота.

В onSuccess в объекте-параметре метода свойство address будет содержать адрес, полученный по указанным координатам.

В onFailure коды ошибки могут быть такими:

  • 0: Нет ошибок
  • 6: Доступ запрещен
  • 7: У приложения есть сообщения в очереди
  • 8: Приложение временно в черном списке

Более подробно о методе getReverseLocation можно узнать здесь.

Скачать исходный код примера.

Previous ArticleNext Article
Технический директор IT-Dimension, компании-разработчика кросс-платформенного программного обеспечения

Leave a Reply

Your email address will not be published. Required fields are marked *

О.

Обмен данными между сценами приложения для Palm WebOS с помощью Cookies

Очень часто в приложении необходимо передать данные, введенные пользователем, из одной формы в другую. В Palm WebOS для этих целей можно использовать Cookies. API для использования Cookies довольно прозрачно, как и многие другие вещи в WebOS, не требует особых умений.

В этот раз мы рассмотрим пример того, как различные сцены могут обмениваться данными.

Для начала создадим новое приложение, в нем новую сцену с названием Main и еще одну с названием Second. Как создать проект и сцену можно узнать из этой статьи.

Напишем HTML-шаблоны сцен:

views/main/main-scene.html

<div id="main" class="palm-hasheader">
<div class="palm-header">Main</div>
<div id="SecondSceneButton" x-mojo-element="Button"></div>
<div id="ExitButton" x-mojo-element="Button"></div>
<div id="counter" class="palm-body-text"></div>
</div>

views/second/second-scene.html

<div id="main" class="palm-hasheader">
     <div class="palm-header">Second Scene</div>
     <div id="CloseButton" x-mojo-element="Button"></div>
     <div id="valuetext" class="palm-body-text">Unknown value</div>
</div>

Главная сцена состоит из контейнера с заголовком, двух кнопок и текстового поля.
Вторая сцена состоит из контейнера с заголовком, кнопки и текстового поля.
Ничего сверхъестественного. 🙂

Перед тем, как написать логику, стоит ознакомиться со схемой, описывающей жизненный цикл сцены:

WebOS Scene Lifecycle

Из схемы можно узнать, что первым делом вызывается конструктор сцены, затем метод setup(), а когда сцена загрузилась, вызывается метод activate(). Также activate() вызывается при каждом появлении сцены на экране.

Что это нам даст? Посмотрим на пример кода ниже:

MainAssistant.prototype.setup = function() {
  this.controller.setupWidget("SecondSceneButton",
    {}, {label:"Second Scene"});
  this.controller.setupWidget("ExitButton",
    {}, {label:"Exit"});

  Mojo.Event.listen(this.controller.get("SecondSceneButton"),
    Mojo.Event.tap,
    this.handleSecondSceneButton.bind(this));
  Mojo.Event.listen(this.controller.get("ExitButton"),
    Mojo.Event.tap,
    this.handleExitButton.bind(this));

  this.counter = 0;
  this.counterCookie = new Mojo.Model.Cookie("counterCookie");
  this.counterCookie.put(this.counter);
}

MainAssistant.prototype.activate = function(event) {
  this.counter = this.counterCookie.get();
  this.controller.get("counter").update(this.counter);
}

MainAssistant.prototype.cleanup = function(event) {
  this.counterCookie.remove();
}

В методе setup() мы создали переменную counter, поместили ее в Cookie с названием counterCookie, а в методе activate() мы берем значение из Cookie и отображаем в текстовом поле. И каждый раз, когда форма будет активизироваться, в текстовом поле будет появляться актуальное значение из Cookie.
Метод cleanup() вызывается в конце жизненного цикла сцены и в нем мы удаляем созданную Cookie.

Теперь напишем обработчик нажатия кнопки SecondSceneButton:

MainAssistant.prototype.handleSecondSceneButton = function(event) {
  this.counter++;
  this.counterCookie.put(this.counter);
  this.controller.stageController.pushScene("second", 1);
}

В обработчике мы прибавляем единицу к переменной counter, записываем ее значение в Cookie и отображаем сцену Second.

Дальше нам необходимо получить значение в сцене Second и как-то там поиспользовать.

function SecondAssistant() {
}

SecondAssistant.prototype.setup = function() {
  this.controller.setupWidget("CloseButton",
    {}, {label:"Close"});

  Mojo.Event.listen(this.controller.get("CloseButton"),
    Mojo.Event.tap,
    this.handleCloseButton.bind(this));
  this.counterCookie = new Mojo.Model.Cookie("counterCookie");
  this.controller.get("valuetext").update(this.counterCookie.get());
}

SecondAssistant.prototype.handleCloseButton = function(event) {
  var value = this.counterCookie.get();
  value++;
  this.counterCookie.put(value);
  this.controller.stageController.popScene();
}

В методе setup() сцены мы создаем объект Cookie, получаем сохраненное значение и отображаем его в текстовом поле.

В обработчике нажатия кнопки мы берем значение Cookie, прибавляем к нему единицу и записываем обратно в Cookie. Затем закрываем сцену.

После закрытия сцены Second будет отображена главная сцена Main и при активации вызовется метод activate(), в котором измененное значение снова появится в текстовом поле.

После всего наше приложение будет выглядеть так:

WebOS Cookies Test GUI

Скачать исходный код примера.

П.

Пишем для Palm WebOS “на коленке”, создаем проект руками

В этот раз рассмотрим более детально структуру приложения для WebOS. В SDK есть достаточное количество дополнительных утилит, кторые могут помочь сгенерировать приложение или сцену. Но, при использовании автоматических кодогенераторов, понимания того, как же всетаки устроен проект, не прибавляется. Если кодогенератор отработает некорректно по какой-либо причине, без знания структуры проекта вернуть все в рабочее состояние будет непросто.

Для начала сделаем вот что: создадим пустой проект. В Eclipse идем в File -> New -> Other, в диалоговом окне выбираем General -> Project. Далее в мастере указываем имя проекта и путь, где он будет создан.

Создаем файл описания приложения

Создаем пустой файл в проекте, называем его appinfo.json

{
	"id": "com.itdimension.manuallycreatedwebosproject",
	"version": "1.0.0",
	"vendor": "Mobile-Developer.ru",
	"type": "web",
	"main": "index.html",
	"title": "Manually Created Project",
	"icon": "icon.png"
}

В этом файле указаны:

  • id – уникальный идентификатор приложения
  • version – версия приложения
  • vendor – название производителя
  • type – тип проекта
  • main – начальная страница приложения
  • title – название проекта (оно будет использовано для отображения в списке установленных  приложений WebOS)
  • icon – иконка приложения (также будет использоваться при отображении в Launcher’е)

Как видно, файл настроек приложения содержит упоминание двух файлов – файла главной страницы, а также иконки приложения. Узнать больше о параметрах, которые можно указать в appinfo.js можно здесь. Интересным также является тот факт, что кроме web-приложений можно создавать и нативные приложения. Для таких приложений свойство type будет иметь значение native.

Создаем файл главной страницы

В проекте Eclipse создаем новый html-файл, называем его index.html.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
	"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
	<title>CameraTest</title>
	<script 
		src="/usr/palm/frameworks/mojo/mojo.js" 
		type="text/javascript" x-mojo-version="1" />
	<link
		href="stylesheets/cameratest.css" 
		media="screen" rel="stylesheet" type="text/css" />
</head>
<body>
	<h2>Manually Created Project</h2>
</body>
</html>

Если в приложении будут использоваться нестандартные CSS-стили элементов управления, то они должны быть указаны обязательно ПОСЛЕ ссылки на mojo.js.

И вот, в данный момент у нас есть каркас минимального приложения, которое можно запустить на эмуляторе. Но прежде чем его запустить, нужно создать пакет, который будет установлен на эмулятор (или на устройство).

Для создания пакета в Palm Mojo SDK есть скрипт palm-package. Нам достаточно скормить этому скрипту путь к папке с проектом и на выходе получим .ipk файл с приложением.

palm-package ManuallyCreatedWebOSProject

В результате выполнения этой команды у нас должен получиться файл com.itdimension.manuallycreatedwebosproject_1.0.0_all.ipk
Для того чтобы загрузить пакет на эмулятор необходимо вызвать скрипт palm-install и скормить ему .ipk файл

D:\Projects\Eclipse Projects>palm-install com.itdimension.manuallycreatedwebosproject_1.0.0_all.ipk

После этого в эмуляторе запускаем Launcher и видим в нем наше приложение.
WebOS Manually Created Application in Launcher
После запуска приложения получим вот такой экран:
WebOS Manually Created App
Теперь надо бы сделать так, чтобы в нашем приложении кроме главной страницы было еще что-нибудь.

Создаем сцену

Для начала надо создать структуру каталогов:

  • app
    • assistants – в ней юудут находиться js-файлы, содержащие логику управления сценами
    • views – в ней будут содержаться html-файлы сцен

В WebOS-приложении есть понятие “stage” – окно и “scene” – панель в окне. Для каждой сущности нужен свой assistant-класс с обработчиками событий.

stage-assistant.js

function StageAssistant() {
}

StageAssistant.prototype.setup = function() {
    this.controller.pushScene("main");
}

Как видим, в конструкторе assistant-класса мы отображаем сцену с именем main. Теперь создадим саму сцену:

В папке views создаем папку main и в ней файл main-scene.html

<div id="main" class="palm-hasheader">
    <div class="palm-header">Camera Test</div>
    <div id="MyButton" x-mojo-element="Button"></div>
</div>

В папке assistants создаем main-assistant.js

function MainAssistant() {
}

MainAssistant.prototype.setup = function() {
	this.controller.setupWidget("MyButton", {}, {label:"My Button"});
}

Теперь создадим файл sources.json, в котором будут регистрироваться файлы с assistant-классами.

[
    {"source": "app\/assistants\/stage-assistant.js"},
    {
        "source": "app\/assistants\/main-assistant.js",
        "scenes": "main"
    }
]

В результате после запуска приложения получим такой пользовательский интерфейс:
WebOS Manually Created Scene

Скачать исходный код примера.