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

В этот раз речь пойдет об использовании сервиса Connection Manager, который позволяет определить состояние подключения к сети. В WebOS существует возможность узнать состояние подключения к:

  • Bluetoot
  • Wi-Fi
  • WAN

Использовать сервис Connection Manager можно как для одноразового получения состояния сетевых подключений, так и для подписки на уведомление об изменении состояния подключений.Для определения текущего состояния сетевых соединений необходимо вызвать метод serviceRequest() объекта-контроллера, в который передать идентификатор сервиса Connection Manager, а также JSON-объект с параметрами вызова сервиса. Параметры вызова сервиса могут быть такими:

  • method – обязательный параметр. У сервиса Connection Manager есть всего один метод – getstatus.
  • parameters – JSON-объект, который будет передан в качестве параметра методу getstatus. У этого объекта есть одно значимое булевое свойство – subscribe. Если свойство subscribe имеет значение true, то будет выполнена подписка на уведомления об изменении состояния подключений. При каждом изменении состояния будет вызвана наша callback-функция.
  • onSuccess – callback-функция, вызываемая при успешном завершении запроса состояния подключений.
  • onFailure – callback-функция, вызываемая если запрос состояния подключений завершился с ошибкой.

Пример вызова Connection Manager

this.controller.serviceRequest('palm://com.palm.connectionmanager', {
    method: 'getstatus',
    parameters: {subscribe:true},
    onSuccess: this.handleOKResponse.bind(this),
    onFailure: this.handleErrResponse
});

Теперь рассмотрим более детально callback-функции, вызываемые при завершении запроса состояния соединения.

onSuccess

Callback-функция onSuccess принимает в качестве параметра объект, содержащий информацию о состоянии подключений. Этот объект содержит такие поля:

Свойство Тип Описание
errorCode integer Код завершения операции проверки статуса подключения.
errorText string Описание ошибки если errorCode содержит ненулевое значение.
isInternetConnectionAvailable boolean Содержит значение true если есть подключение к Internet.
returnValue boolean true если была выполнена подписка на уведомление об изменении состояния подключений. Может быть получено только первый раз при выполнении подписки.
wifi object Содержит информацию о подключении к Wi-Fi.
btpan object Содержит информацию о подключении к Bluetooth.
wan object Содержит информацию о подключении к WAN.

Теперь посмотрим что можно узнать о подключениях:

Свойства объекта btpan
Свойство Тип Описание
ipAddress string IP-адрес в сети Bluetooth PAN.
panUser string Содержит название клиента сети Bluetooth PAN, подключенного к устройству.
state string Имеет значение “connected” если соединение есть и “disconnected” если соединения нет.
Свойства объекта wifi
Свойство Тип Описание
bssid string Basic service set identifier (BSSID) точки доступа.
ipAddress string IP-адрес в сети Wi-Fi.
ssid string Service set identifier (BSSID) точки доступа.
state string Имеет значение “connected” если соединение есть и “disconnected” если соединения нет.
Свойства объекта wan
Свойство Тип Описание
ipAddress string IP-адрес в сети WAN.
network string Сожет иметь значения: “unknown”, “unusable”, “gprs”, “edge”, “umts”, “hsdpa”, “1x”, or “evdo.”
Внимание: значение “unusable” используется в случае если интерфейс WAN подключен, но данніе не могут быть отправлены или получены по каким-либо причинам.
state string Имеет значение “connected” если соединение есть и “disconnected” если соединения нет.

onFailure

Callback-функция onFailure принимает в качетве параметра объект, содержащий информацию об ошибке. Свойства этого объекта приведены ниже:

Свойство Тип Описание
errorCode string Строка, содержащая код ошибки:

  1. Неизвестная ошибка.
  2. Ошибка подписки на уведомления о состоянии соединения.
errorText string Строка, содержащая описание ошибки.
returnValue boolean false

Пример использования

С теоретической частью мы закончили. Теперь рассмотрим пример.

Создадим новый проект и в нем сцену с названием first.

app/views/first/first-scene.html

<div id="SubscribeButton" x-mojo-element="Button"></div>
<div id="area-to-update"></div>
<div class="palm-inline-error" style="display:none" id="ErrorMessage">
<div class="error-message">Unable to subscribe to Connection Manager's event.</div>
</div>

<div class="palm-group">
  <div class="palm-group-title">Wi-Fi</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">IP Address</div>
          <div id="WiFiIPTextBox" x-mojo-element="TextField"></div>
        </div>
      </div>
    </div>
    <div class="palm-row">
      <div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
        <div class="title">
          <div class="label">BSSID</div>
          <div id="WiFiBSSIDTextBox" x-mojo-element="TextField"></div>
        </div>
      </div>
    </div>
    <div class="palm-row">
      <div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
        <div class="title">
          <div class="label">SSID</div>
          <div id="WiFiSSIDTextBox" 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">State</div>
          <div id="WiFiStateTextBox" x-mojo-element="TextField"></div>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="palm-group">
  <div class="palm-group-title">Bluetooth</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">IP Address</div>
          <div id="BTPanIPTextBox" x-mojo-element="TextField"></div>
        </div>
      </div>
    </div>
    <div class="palm-row">
      <div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
        <div class="title">
          <div class="label">User</div>
          <div id="BTPanUserTextBox" 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">State</div>
          <div id="BTPanStateTextBox" x-mojo-element="TextField"></div>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="palm-group">
  <div class="palm-group-title">WAN</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">IP Address</div>
          <div id="WanIPTextBox" x-mojo-element="TextField"></div>
        </div>
      </div>
    </div>
    <div class="palm-row">
      <div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
        <div class="title">
          <div class="label">Network</div>
          <div id="WanNetworkTextBox" 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">State</div>
          <div id="WanStateTextBox" x-mojo-element="TextField"></div>
        </div>
      </div>
    </div>
  </div>
</div>

app/assistants/first-assistant.js

function FirstAssistant() {
}

FirstAssistant.prototype.setup = function() {
    this.controller.setupWidget('SubscribeButton', {}, {label:'Subscribe'});
    this.controller.setupWidget("BTPanIPTextBox",
        {
            modelProperty: 'originalValue'
        },
        this.btpanIPModel =
        {
            originalValue: '',
            disabled: true
        });
    this.controller.setupWidget("BTPanUserTextBox",
        {
            modelProperty: 'originalValue'
        },
        this.btpanUserModel =
        {
            originalValue: '',
            disabled: true
        });
    this.controller.setupWidget("BTPanStateTextBox",
        {
            modelProperty: 'originalValue'
        },
        this.btpanStateModel =
        {
            originalValue: '',
            disabled: true
        });

    this.controller.setupWidget("WiFiIPTextBox",
        {
            modelProperty: 'originalValue'
        },
        this.wifiIPModel =
        {
            originalValue: '',
            disabled: true
        });
    this.controller.setupWidget("WiFiBSSIDTextBox",
        {
            modelProperty: 'originalValue'
        },
        this.wifiBSSIDModel =
        {
            originalValue: '',
            disabled: true
        });
    this.controller.setupWidget("WiFiSSIDTextBox",
    {
            modelProperty: 'originalValue'
        },
        this.wifiSSIDModel =
        {
            originalValue: '',
            disabled: true
        });
    this.controller.setupWidget("WiFiStateTextBox",
        {
            modelProperty: 'originalValue'
        },
        this.wifiStateModel =
        {
            originalValue: '',
            disabled: true
        });

    this.controller.setupWidget("WanIPTextBox",
        {
            modelProperty: 'originalValue'
        },
        this.wanIPModel =
        {
            originalValue: '',
            disabled: true
        });
    this.controller.setupWidget("WanNetworkTextBox",
        {
            modelProperty: 'originalValue'
        },
        this.wanNetworkModel =
        {
            originalValue: '',
            disabled: true
        });
    this.controller.setupWidget("WanStateTextBox",
        {
            modelProperty: 'originalValue'
        },
        this.wanStateModel =
        {
            originalValue: '',
            disabled: true
        });

    Mojo.Event.listen(this.controller.get('SubscribeButton'),
        Mojo.Event.tap, this.handleSubscribeButton.bind(this));
}

FirstAssistant.prototype.handleSubscribeButton = function(){
    this.controller.serviceRequest('palm://com.palm.connectionmanager', {
        method: 'getstatus',
        parameters: {subscribe:true},
        onSuccess: this.handleOKResponse.bind(this),
        onFailure: this.handleErrResponse
    });
}

FirstAssistant.prototype.handleOKResponse = function(response){
    this.controller.get('ErrorMessage').hide();
    this.controller.get('ErrorMessage').setStyle({visibility:'hidden'});

    this.btpanStateModel.originalValue = response.btpan.state;
    this.btpanIPModel.originalValue = response.btpan.ipAddress;
    this.btpanUserModel.originalValue = response.btpan.panUser;
    this.controller.modelChanged(this.btpanStateModel);
    this.controller.modelChanged(this.btpanIPModel);
    this.controller.modelChanged(this.btpanUserModel);

    this.wifiStateModel.originalValue = response.wifi.state;
    this.wifiIPModel.originalValue = response.wifi.ipAddress;
    this.wifiSSIDModel.originalValue = response.wifi.ssid;
    this.wifiBSSIDModel.originalValue = response.wifi.bssid;
    this.controller.modelChanged(this.wifiStateModel);
    this.controller.modelChanged(this.wifiIPModel);
    this.controller.modelChanged(this.wifiSSIDModel);
    this.controller.modelChanged(this.wifiBSSIDModel);

    this.wanStateModel.originalValue = response.wan.state;
    this.wanIPModel.originalValue = response.wan.ipAddress;
    this.wanNetworkModel.originalValue = response.wan.network;
    this.controller.modelChanged(this.wanStateModel);
    this.controller.modelChanged(this.wanIPModel);
    this.controller.modelChanged(this.wanNetworkModel);
}

FirstAssistant.prototype.handleErrResponse = function(response){
    this.controller.get('ErrorMessage').show();
    this.controller.get('ErrorMessage').setStyle({visibility:'visible'});
}

FirstAssistant.prototype.cleanup = function(event) {
    Mojo.Event.stopListening(this.controller.get("SubscribeButton"),
        Mojo.Event.tap, this.handleSubscribeButton);
}

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

Также сцена содержит три группы контролов (текстовых полей), в которых, по приходу уведомления, будет отображаться информация о подключениях.

WebOS Connection Manager Test

В обработчике onSuccess мы получаем значения всех полей из объектов wan, wifi и btpan и отображаем их в соответствующих текстовых полях.

В эмуляторе валидной будет информация только о подключении к Wi-Fi. Другие сетевые подключения на данный момент не доступны под эмулятором.

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

З.

Загрузка файлов на сервер из приложения в Palm WebOS

В этот раз мы узнаем, как работать Download Manager API, которое появилось в новой версии Mojo SDK v1.2. В этой статье рассматривается возможность загрузки файлов с устройства на сервер с помощью Download Manager API.

Допустим, у нас есть Web-сервис, позволяющий загружать файлы с описанием (напримепр, фотографии). И вот, наше приложение должно обеспечить работу с этим сервисом для устройств, работающих под управлением WebOS.

Вот у нас есть простенькая HTML-форма для загрузки файлов:

upload.html

<form enctype="multipart/form-data" action="uploadtest.php" method="POST">
  <table border="0" cellspacing="5">
    <tr>
      <td>Info:</td>
      <td><input type="text" name="info" /></td>
    </tr>
    <tr>
      <td>File:</td>
      <td><input type="file" name="filename" /></td>
    </tr>
    <tr>
      <td colspan="2" align="center"><input type="submit" /></td>
    </tr>
  </table>
</form>

Также есть простенький Web-сервис, который принимает файлы и выполняет проверку по размеру, а также наличие описания. Выдачу результатов загрузки пользователь получает в HTML-виде:

uploadtest.php

<?php
do
{
    $max_file_size = 1024 * 100;
    $info = $_POST['info'];
    if(!$info) break;
    echo "Info - OK<br>";
    $filename = $_FILES['filename']['tmp_name'];
    $uploaded_size = filesize($filename);
    echo "File size: $uploaded_size<br>";
    if($uploaded_size == 0) break;
    echo "File size > 0<br>";
    if($uploaded_size > $max_file_size) break;
    echo "File size < MAX<br>";
    echo "PROFIT !!! : )";
    exit();
}
while(false);
echo "Error";
?>

В случае успешной загрузки файла на сервер мы получим в выдаче что-то подобное:

Info – OK
File size: 5092
File size > 0
File size < MAX
PROFIT !!! : )

И вот, мы сейчас узнаем как аналогичный функционал получить в приложении для Palm WebOS.

Создадим новое приложение и в нем сцену main.

app/views/main/main-scene.html

<div class="palm-group">
  <div class="palm-group-title">File info</div>
  <div class="palm-row first">
    <div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
      <div class="title">
      	<div class="label">File</div>
        <div id="FileTextBox" 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">Info</div>
        <div id="InfoTextBox" x-mojo-element="TextField"></div>
      </div>
    </div>
  </div>
</div>
<div id="SelectFileButton" x-mojo-element="Button"></div>
<div id="UploadButton" x-mojo-element="Button"></div>
<div id="ExitButton" x-mojo-element="Button"></div>
<div id="info" class="palm-body-text"></div>

Сцена содержит группу с двумя текстовыми полями. В первом поле можно ввести имя файла, который будет загружен на сервер, а во втором – текстовое описание. Также на сцене находятся три кнопки: выбор файла, загрузка, выход.

app/assistants/main-assistant.js

function MainAssistant() {
}

MainAssistant.prototype.setup = function() {
    this.controller.setupWidget("FileTextBox",
        {
            modelProperty: 'value'
        },
        this.fileModel =
        {
            value: ''
        });
    this.controller.setupWidget("InfoTextBox",
        {
            modelProperty: 'value',
            autoFocus: true
        },
        this.infoModel =
        {
            value: ''
        });
    this.controller.setupWidget("SelectFileButton",
        {}, this.selectFileModel = {label: "Select File"});
    this.controller.setupWidget("UploadButton",
        {
            type: Mojo.Widget.activityButton
        },
        this.uploadButtonModel =
        {
            label: "Upload",
            disabled: true
        });
    this.controller.setupWidget("ExitButton", {}, {label: "Exit"});

    Mojo.Event.listen($(SelectFileButton), Mojo.Event.tap, this.onSelectFile.bind(this));
    Mojo.Event.listen($(UploadButton), Mojo.Event.tap, this.onUpload.bind(this));
    Mojo.Event.listen($(ExitButton), Mojo.Event.tap, this.onExit.bind(this));
    Mojo.Event.listen($(FileTextBox), Mojo.Event.propertyChange,
        this.onPropertyChanged.bind(this));
    Mojo.Event.listen($(InfoTextBox), Mojo.Event.propertyChange,
        this.onPropertyChanged.bind(this));
}

MainAssistant.prototype.onPropertyChanged = function(value) {
}

MainAssistant.prototype.onSelectFile = function() {
}

MainAssistant.prototype.onUpload = function() {
}

MainAssistant.prototype.onExit = function() {
    this.controller.stageController.getAppController().closeAllStages();
    window.close();
}

MainAssistant.prototype.cleanup = function(event) {
    Mojo.Event.stopListening($(SelectFileButton), Mojo.Event.tap, this.onSelectFile);
    Mojo.Event.stopListening($(UploadButton), Mojo.Event.tap, this.onUpload);
    Mojo.Event.stopListening($(ExitButton), Mojo.Event.tap, this.onExit);
}

Кнопка Upload имеет тип Activity Button – при нажатии на такой кнопке появляется индикатор активности.

К текстовым полям мы подключили обработчик onPropertyChanged(), который будет вызываться при вводе текста с клавиатуры.

В методе cleanup() мы отключаем все обработчики событий от виджетов. Это необходимо делать всегда для того чтобы не ьыло утечек памяти.

В результате у нас должно получиться такое:

WebOS Uploader GUI

Теперь можно переходить к реализации логики работы приложения.

Во-первых, надо обеспечить проверку ввода данных пользователем. Мы не можем запускать загрузку до того, как пользователь введет имя файла и описание в текстовые поля. Для проверки введенных значений у нас есть метод onPropertyChanged().

MainAssistant.prototype.onPropertyChanged = function(value) {
    do
    {
        if(this.fileModel.value.length == 0) break;
        if(this.infoModel.value.length == 0) break;
        if(this.uploadButtonModel.disabled)
        {
            this.uploadButtonModel.disabled = false;
            this.controller.modelChanged(this.uploadButtonModel);
        }
        return;
    }
    while(false);
    if(!this.uploadButtonModel.disabled)
    {
        this.uploadButtonModel.disabled = true;
        this.controller.modelChanged(this.uploadButtonModel);
    }
}

Теперь надо дать возможность пользователю выбрать файл для загрузки. Сделать это можно с помощью виджета FilePicker.

MainAssistant.prototype.onSelectFile = function() {
    Mojo.FilePicker.pickFile(
        {
            onSelect: this.onFileSelected.bind(this)
        },
        this.controller.stageController);
}

MainAssistant.prototype.onFileSelected = function(result) {
this.fileModel.value = result.fullPath;
this.controller.modelChanged(this.fileModel);
this.onPropertyChanged();
}

Webos FilePicker

К сожалению, после того, как мы изменили значение в модели и вызвали modelChanged(), метод onPropertyChanged() автоматически не вызывается, поэтому нам приходится вызывать его руками.

Теперь надо реализовать логику отправки файла:

MainAssistant.prototype.onUpload = function() {
    try
    {
        var posturl = "http://192.168.0.1/uploadtest.php";
        this.controller.serviceRequest('palm://com.palm.downloadmanager/',
            {
                method: 'upload',
                parameters:
                {
                    fileName: this.fileModel.value,
                    fileLabel: 'filename',
                    url: posturl,
                    contentType: 'application/x-www-form-urlencoded',
                    subscribe: true,
                    postParameters:
                    [
                        {key: 'info' , data: this.infoModel.value}
                    ]
                },
                onSuccess : this.onUploadStatus.bind(this),
                onFailure: function(e)
                {
                    this.uploadTicket = 0;
                    this.enableControls(true);
                    this.uploadButtonModel.disabled = false;
                    this.controller.modelChanged(this.uploadButtonModel);
                    $(UploadButton).mojo.deactivate();
                    Mojo.Controller.errorDialog("Upload failed, " + e.errorCode + " : " + e.errorText);
                }
            });
    }
    catch(e)
    {
        Mojo.Controller.errorDialog(e);
    }
}

Для отправки файла на сервер мы испотльзуем метод upload сервиса Download Manager. Параметры у этого метода такие:

  • fileName – имя файла на устройстве
  • fileLabel – это значение соответствует атрибута name тега input в HTML-форме.
  • url – адрес скрипта, которому будет отправлен запрос
  • subscribe – если указать значение этого параметра равным true, то мы выполним подписку на уведомления о состоянии загрузки. Это мы и сделали
  • onSuccess – функция, которая будет вызываться при получении уведомления о состоянии загрузки
  • onFailure – функция, которая будет вызвана в случае ошибки загрузки
  • postParameters – массив, содержащий POST-параметры запроса. Каждый элемент этого массива должен содержать поле key с именем параметра и поле data со значением параметра. В этом массиве мы передаем параметр info (аналогичный input с типом text есть в HTML-форме).
MainAssistant.prototype.enableControls = function(value) {
this.fileModel.disabled = !value;
this.infoModel.disabled = !value;
this.selectFileModel.disabled = !value;
this.controller.modelChanged(this.fileModel);
this.controller.modelChanged(this.infoModel);
this.controller.modelChanged(this.selectFileModel);
}

MainAssistant.prototype.onUploadStatus = function(response) {
    if(response.subscribed)
    {
        this.enableControls(false);
        this.uploadButtonModel.disabled = true;
        this.controller.modelChanged(this.uploadButtonModel);
        $(UploadButton).mojo.activate();
        this.uploadTicket = response.ticket;
    }
    else if(response.completed)
    {
        this.uploadTicket = 0;
        this.enableControls(true);
        this.uploadButtonModel.disabled = false;
        this.controller.modelChanged(this.uploadButtonModel);
        $(UploadButton).mojo.deactivate();

        $(info).update(response.responseString);
    }
}

Как видно из кода, в функции, которая отрабатывает при получении состояния загрузки, мы проверяем значение поля subscribe объекта-параметра, который содержит всю информацию о загрузке. Поле subscribe содержит значение true только когда загрузка успечно началась. Если мы получаем true в поле subscribe, то деактивируем все текстовые поля, а также кнопку Select File и кнопку Upload. Также  мы отображаем индикатор на кнопке Upload с помощью метода activate().

Когда мы получаем значение true в поле completed, это значит что загрузка завершилась. Мы активируем виджеты и убираем индикатор с кнопки Upload с помощью метода deactivate(). Затем мы отображаем ответ от сервера на сцене.

Кроме всего прочего нам необходимо обработать ситуацию, когда приложение завершается до того, как файл загружен на сервер. После начала загрузки мы можем получить уникальный идентификатор загрузки с помощью поля ticket объекта, возвращаемого сервисом Download Manager. В методе cleanup() сцены нам необходимо проверить это значение, если оно ненулевое, то завершить загрузку принудительно.

MainAssistant.prototype.cleanup = function(event) {
        Mojo.Event.stopListening($(SelectFileButton), Mojo.Event.tap, this.onSelectFile);
        Mojo.Event.stopListening($(UploadButton), Mojo.Event.tap, this.onUpload);
        Mojo.Event.stopListening($(ExitButton), Mojo.Event.tap, this.onExit);
        if(this.uploadTicket != 0)
        {
                var handler = function(res) { this.uploadTicket = 0 }
                this.controller.serviceRequest('palm://com.palm.downloadmanager/',
                {
                    method: 'cancelDownload',
                    parameters:
                    {
                        ticket : this.uploadTicket
                    },
                    onSuccess : handler.bind(this),
                    onFailure : handler.bind(this)
                });
    }
}

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