Для начала давайте разберемся, что такое Depot и зачем он может понадобиться в приложениях для Palm WebOS.

Если ваше приложение должно хранить данные, то в таком случае есть три варианта:

  • Использование Cookies
  • Использование базы данных SQLite
  • Использование Depot

Но в каких же случаях есть смысл использовать каждый из перечисленных вариантов? Если необходимо сохранить небольшой объем данных, например настройки приложения, то лучше использовать Cookies. Если же объем данных относительно большой, то выбор должен быть сделан в пользу одного из оставшихся способов. Какой из них лучше?

Что лучше использовать – зависит от объема данных, которые нужно хранить, а также от того, где вы будете хранить эти данные. Если вы используете хранилище по умолчанию (кстати Depot переводится как “хранилище” или “склад”), то у вас объем данных будет ограничен одним мегабайтом.

Если вам необходимо хранить данные в структурированном виде, то лучше сделать выбор в пользу базы данных SQLite.

Кстати, ограничение на 1 МБ данных можно обойти, разместив хранилище на разделе с медиа. В этом случае размер хранилища будет ограничен только объемом свободной памяти на это мразделе.

В чем же отличие Depot от базы данных SQLite? Depot может хранить только объекты. Например сделать так чтобы хранилась только булевая переменная не получится, нужно обязательно сделать объект и эту булевую переменную указать в виде свойства.

{ "value" : true }

Тоесть, что получается: Depot – это неструктурированное хранилище объектов. На самом деле это просто еще один уровень абстракции над API по работе с базами данных SQLite, но он позволяет избавиться от необходимости писать SQL в коде, что довольно удобно.

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

app/views/main/main-scene.html

<div id="main" class="palm-hasheader">
  <div class="palm-header">Depot Test</div>
  <div id="AddSceneButton" x-mojo-element="Button"></div>
  <div id="info" x-mojo-element="x-body-text"></div>
</div>

app/assistants/main-assistant.js

function MainAssistant() {
}

MainAssistant.prototype.setup = function() {
  this.controller.setupWidget("AddSceneButton", {}, {label:"Add"});
  Mojo.Event.listen(this.controller.get("AddSceneButton"),
    Mojo.Event.tap,
    this.handleAddSceneButton.bind(this));
}

MainAssistant.prototype.handleAddSceneButton = function(event) {
  this.controller.stageController.pushScene("add");
}

app/views/add/add-scene.html

<div id="main" class="palm-hasheader">
  <div class="palm-header left" id="list-header">
    Add / replace data
  </div>
</div>
<div class="palm-group">
  <div class="palm-group-title"><span x-mojo-loc="">Database Info.</span></div>
    <div class="palm-list">
      <div class='palm-row first'>
        <div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
          <div class="title">
            <div class="label">Key</div>
            <div id="keyField" name="keyField" x-mojo-element="TextField" ></div>
          </div>
        </div>
      </div>
      <div class='palm-row last'>
        <div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
          <div class="title">
            <div class="label">Text</div>
            <div id="textField" name="textField" x-mojo-element="TextField" ></div>
          </div>
        </div>
      </div>
    </div>
</div>
</div>
<div id="depotResult" class="info-text"></div>
<div x-mojo-element="Button" id="add_button"></div>
<div x-mojo-element="Button" id="back_button"></div>

app/assistants/add-assistant.js

function AddAssistant() {
}

AddAssistant.prototype.setup = function() {
  this.controller.setupWidget('keyField',
    {
      hintText:       'Enter key here',
      modelProperty:  'originalValue',
      textCase:       Mojo.Widget.steModeLowerCase,
      focus:          true,
      maxLength:      30
    },
    {
      originalValue : ''
    });
  this.controller.setupWidget('textField',
    {
      hintText:       'and some text here',
      modelProperty:  'originalValue',
      textCase:       Mojo.Widget.steModeLowerCase,
      multiline:      false,
      focus:          false,
      maxLength:      30
    },
    {
      originalValue : ''
    });
  this.controller.setupWidget("add_button", {}, {label:"Add entry"});
  this.controller.setupWidget("back_button", {}, {label:"Back to main view"});

  Mojo.Event.listen(this.controller.get("add_button"),
    Mojo.Event.tap, this.handleAddButton.bind(this));
  Mojo.Event.listen(this.controller.get("back_button"),
    Mojo.Event.tap, this.handleBackButton.bind(this));
}

AddAssistant.prototype.cleanup = function() {
  this.controller.stopListening(
    this.controller.get('add_button'), Mojo.Event.tap,
    this.handleAddButton);
  this.controller.stopListening(
    this.controller.get('back_button'), Mojo.Event.tap,
    this.handleBackButton);
}

AddAssistant.prototype.deactivate = function(event) {
}

AddAssistant.prototype.handleAddButton = function(event) {
}

AddAssistant.prototype.handleBackButton = function(event) {
  this.controller.stageController.popScene();
}

На главной форме у нас находится кнопка Add, которая вызывает сцену, в которой можно добавить новые записи в Depot.
Форма добавления новых записей содержит заголовок, группу из двух текстовых полей и две кнопки – кнопку добавления новой записи и кнопку закрытия сцены.
WebOS Depot Test

Ну вот, GUI готово, теперь можем начинать работу с Depot.

function MainAssistant() {
  var options = {
    name: "depot_tutorial", //Название хранилища (обязательный параметр)
    version: 1, //Версия базы данных. (опционально, по умолчанию 1)
    replace: false // открывать существующее хранилище
  };
  this.depot = new Mojo.Depot(options,
    this.dbConnectionSuccess.bind(this),
    this.dbConnectionFailure.bind(this));
}

MainAssistant.prototype.dbConnectionSuccess = function() {
    this.controller.get("info").update('OK');
}

MainAssistant.prototype.dbConnectionFailure = function() {
    this.controller.get("info").update('Error');
}

При создании объекта Depot можно указать методы, которые будут отрабатывать при успешном подключении к хранилищу или же при ошибке подключения.

Если надо создать хранилище на разделе с медиа, то название хранилища будет выглядеть так:

name: "ext:depot_tutorial"

У нас есть сцена, в которой должно происходить добавление объектов в Depot. Нам надо передать в нее объект Depot в качестве параметра. Сделать это можно вот как:
app/assistants/main-assistant.js

MainAssistant.prototype.handleAddSceneButton = function(event)
{
    this.controller.stageController.pushScene("add", this.depot);
}

app/assistants/add-assistant.js

function AddAssistant(arg) {
    this.depot = arg;
}

Мы передаем объект Depot в качестве параметра pushScene() и этот объект будет получен сценой как параметр конструктора.
Теперь нам необходимо написать обработчик кнопки add_button, в котором значение из текстового поля TEXT будет добавляться в хранилище с ключем KEY.

AddAssistant.prototype.handleAddButton = function(event) {
    this.data = { "value" :this.textModel.originalValue };
    this.depot.simpleAdd(this.keyModel.originalValue,
        this.data, this.dbAddSuccess, this.dbAddFailure);
}

AddAssistant.prototype.dbAddSuccess = function(event) {
    this.depotResult.innerHTML = ("Add success!");
}

AddAssistant.prototype.dbAddFailure = function(event) {
    this.depotResult.innerHTML = ("Add failure!");
}

В обработчике кнопки Add мы создаем объект, в нем в свойстве value запоминаем введенный текст и затем записываем в Depot. Запись происходит путем вызова метода simpleAdd(), в котором мы указываем ключ, сохраняемый объект, а также два callback-метода, которые будут вызваны при удачном или неудачном завершении операции записи.

И вот, мы научились добавлять объекты в Depot. Теперь попробуем научиться их оттуда доставать.

Создадим новую сцену с названием get.

app/views/get/get-scene.html

<div id="main" class="palm-hasheader">
  <div class="palm-header left" id="list-header">
    Retrieve data
  </div>
</div>
<div class="info-text">Which data entry you want to get?</div>

<div class="palm-group">
<div class="palm-group-title">Enter key string</div>
  <div class="palm-list">
    <div class="palm-row single">
      <div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
        <div class="title">
          <div id="entryNum" x-mojo-element="TextField" ></div>
        </div>
      </div>
    </div>
  </div>
</div>
<div id="response" class="info-text"></div>
<div x-mojo-element="Button" id="get_button"></div>
<div x-mojo-element="Button" id="back_button"></div>

app/assistants/get-assistant.js

function GetAssistant(arg) {
    this.depot = arg;
}

GetAssistant.prototype.setup = function() {
    this.controller.setupWidget("get_button", {}, {label:"Get value"});
    this.controller.setupWidget("back_button", {}, {label:"Back to main view"});
    this.controller.setupWidget('entryNum',
        {
            hintText:       'Enter key here',
            modelProperty:  'originalValue',
            textCase:       Mojo.Widget.steModeLowerCase,
            multiline:      false,
            focus:          true,
            maxLength: 30
        },
        this.keyModel =
        {
            originalValue : ''
        });

    Mojo.Event.listen(this.controller.get('get_button'),
        Mojo.Event.tap, this.handleGetButton.bind(this));
    Mojo.Event.listen(this.controller.get("back_button"),
        Mojo.Event.tap, this.handleBackButton.bind(this));
}

GetAssistant.prototype.cleanup = function(event) {
    this.controller.stopListening(
        this.controller.get('get_button'), Mojo.Event.tap,
        this.handleGetButton);
    this.controller.stopListening(
        this.controller.get('back_button'), Mojo.Event.tap,
        this.handleBackButton);
}

GetAssistant.prototype.handleGetButton = function(event) {
    this.depot.get(this.keyModel.originalValue,
        this.dbGetSuccess.bind(this),
        this.dbGetFailure.bind(this));
}

GetAssistant.prototype.dbGetSuccess = function(response) {
    var recordSize = Object.values(response).size();
    if(recordSize == 0) {
        this.controller.get("response").innerHTML = "No such record in the database";
    } else {
        this.controller.get("response").innerHTML = "Data entry is: " + response.value;
    }
}

GetAssistant.prototype.dbGetFailure = function(transaction, result) {
    this.controller.get("response").innerHTML = "Error: " + result.message;
}

GetAssistant.prototype.handleBackButton = function(event) {
    this.controller.stageController.popScene();
}

На созданной сцене у нас есть текстовое поле, в которое можно ввести ключ для поиска в хранилище, поле для отображения результатов поиска и две кнопки – кнопка поиска и кнопка закрытия сцены.

WebOS Depot Search GUI

Для получения объекта из хранилища мы используем метод get() объекта Depot. В этом методе указывается ключ и callback-методы, которые отработают при удачном или неудачном завершении транзакции.

На этом все. В этой статье мыузнали:

  • Чем отличается Depot от базы данных SQLite.
  • Как создать объект Depot в хранилище по умолчанию или на разделе с медиа
  • Как создать соединение с Depot
  • Как записать данные в Depot
  • Как получить данные из Depot
  • Как обрабатывать результаты транзакций в callback-методах.

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

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

Leave a Reply

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

Ч.

Что-то странное творится. В новой прошивке для Palm Pre включена возможность Kill Switch

Kill Switch in WebOSДва дня назад Palm выпустила новую версию Palm WebOS, а несколькими днями ранее официально открыла прием приложений в Palm App Catalog. Все хорошо, если бы не одно «но». Оказывается дурные примеры заразительны и Palm, вслед за Google, включила возможность Kill Switch для App Catalog, с помощью которой они смогут удалять приложения, загруженные из App Catalog, удаленно с устройств пользователей.

Palm reserves the right, at its sole discretion and at any time, to add, remove, disable access to, block, or modify the App Catalog, and to add, remove, disable access to, block, or modify remotely any Applications previously downloaded to your device from the App Catalog.

Ну почему, почему все кому ни попадя хотят ковыряться в софте, установленном на моем устройстве? Я негодую!

У.

Учимся работать с GPS в Palm WebOS

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 можно узнать здесь.

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