Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
349 lines
12 KiB
JavaScript
349 lines
12 KiB
JavaScript
/*
|
|
* TransferManager is responsible for:
|
|
* - Handling system message 'activity' while there is an activity request
|
|
* incoming. Then, it will post result/error to requesting app.
|
|
* - Watching Bluetooth enabled/disabled state, then show/hide turn Bluetooth
|
|
* on dialog.
|
|
* - Watching default adapter existed or not. And will post error if we cannot
|
|
* get default adapter.
|
|
* - Watching 'devicePicked', 'cancelSelection' events from DevicePickerPanel
|
|
* module. Then, do corrected activity service flow regarding user decision.
|
|
*
|
|
* @module BluetoothContext
|
|
*/
|
|
define(['require','modules/bluetooth/bluetooth_adapter_manager','modules/bluetooth/bluetooth_context','views/cannot_transfer_dialog','views/device_picker_panel','views/turn_bluetooth_on_dialog'],function(require) {
|
|
'use strict';
|
|
|
|
var AdapterManager = require('modules/bluetooth/bluetooth_adapter_manager');
|
|
var BtContext = require('modules/bluetooth/bluetooth_context');
|
|
var CannotTransferDialog = require('views/cannot_transfer_dialog');
|
|
var DevicePickerPanel = require('views/device_picker_panel');
|
|
var TurnBluetoothOnDialog = require('views/turn_bluetooth_on_dialog');
|
|
|
|
var _debug = false;
|
|
var debug = function() {};
|
|
if (_debug) {
|
|
debug = function bttm_debug(msg) {
|
|
console.log('--> [TransferManager]: ' + msg);
|
|
};
|
|
}
|
|
|
|
var TransferManager = {
|
|
/**
|
|
* An instance to access activity source.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
* @type {Object}
|
|
*/
|
|
_activity: null,
|
|
|
|
/**
|
|
* The init function only watches the 'activity' event from system message.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
*/
|
|
init: function bttm_init() {
|
|
// Watch 'activity' event from system message.
|
|
this._watchActivityRequest();
|
|
},
|
|
|
|
/**
|
|
* Watch 'activity' event with callback function from system message.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
*/
|
|
_watchActivityRequest: function bttm__watchActivityRequest() {
|
|
navigator.mozSetMessageHandler('activity',
|
|
this._activityHandler.bind(this));
|
|
},
|
|
|
|
/**
|
|
* Handle 'activity' request. Then make decision to service the request or
|
|
* reject it.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
* @param {Object} activityRequest
|
|
*/
|
|
_activityHandler: function bttm__activityHandler(activityRequest) {
|
|
debug('_activityHandler(): activityRequest = ' +
|
|
JSON.stringify(activityRequest));
|
|
if (activityRequest.source.data.blobs &&
|
|
activityRequest.source.data.blobs.length > 0) {
|
|
this._activity = activityRequest;
|
|
// Source info is enough to servie file transfer.
|
|
// Start to init and observe Bluetooth relative modules.
|
|
this._observeDefaultAdapter();
|
|
this._observeBluetoothEnabled();
|
|
this._watchEventFromDevicePicker();
|
|
DevicePickerPanel.visible = true;
|
|
} else {
|
|
debug('_activityHandler(): Cannot transfer without blobs data!');
|
|
this._cannotTransfer();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Show alert dialog. End the activity service while a user confirm to close
|
|
* the dialog.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
*/
|
|
_cannotTransfer: function bttm__cannotTransfer() {
|
|
debug('_cannotTransfer(): show alert dialog!!');
|
|
// show an alert with the overdue message
|
|
if (!CannotTransferDialog.isVisible) {
|
|
CannotTransferDialog.showConfirm().then(() => {
|
|
// Post error with reason then end the activity service.
|
|
this._endTransferWithReason('cancelled');
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Post result/error to end the activity service.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
*/
|
|
_endTransferWithReason: function bttm__endTransferWithReason(reason) {
|
|
debug('_endTransferWithReason(): reason = ' + reason);
|
|
if (reason === 'transferred') {
|
|
this._activity.postResult(reason);
|
|
} else if (reason === 'cancelled') {
|
|
this._activity.postError(reason);
|
|
}
|
|
|
|
this._activity = null;
|
|
},
|
|
|
|
/**
|
|
* Observe and get default adapter from bluetooth adapter manager.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
*/
|
|
_observeDefaultAdapter: function bttm__observeDefaultAdapter() {
|
|
// Observe 'defaultAdapter' property for reaching default adapter.
|
|
AdapterManager.observe('defaultAdapter',
|
|
this._onDefaultAdapterChanged.bind(this));
|
|
this._onDefaultAdapterChanged(AdapterManager.defaultAdapter);
|
|
},
|
|
|
|
/**
|
|
* Observe and get 'enabled' property from bluetooth context.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
*/
|
|
_observeBluetoothEnabled: function bttm__observeBluetoothEnabled() {
|
|
// Observe Bluetooth 'enabled' property from hardware side.
|
|
BtContext.observe('enabled', this._onBluetoothEnabledChanged.bind(this));
|
|
this._onBluetoothEnabledChanged(BtContext.enabled);
|
|
},
|
|
|
|
/**
|
|
* 'defaultAdapter' change event handler from adapter manager for
|
|
* updating it immediately.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
* @param {Object} BluetoothAdapter newAdapter
|
|
* @param {Object} BluetoothAdapter oldAdapter
|
|
*/
|
|
_onDefaultAdapterChanged:
|
|
function bttm__onDefaultAdapterChanged(newAdapter, oldAdapter) {
|
|
if (oldAdapter && (newAdapter === null)) {
|
|
debug('_onDefaultAdapterChanged(): Can not get bluetooth adapter!');
|
|
// Post error with reason then end the activity service.
|
|
this._endTransferWithReason('cancelled');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 'enabled' change event handler from bluetooth context for
|
|
* updating UI flow immediately.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
* @param {Boolean} enabled
|
|
*/
|
|
_onBluetoothEnabledChanged:
|
|
function bttm__onBluetoothEnabledChanged(enabled) {
|
|
debug('_onBluetoothEnabledChanged(): enabled = ' + enabled);
|
|
switch(enabled) {
|
|
case undefined:
|
|
break;
|
|
case true:
|
|
// Close the confirmation dialog
|
|
TurnBluetoothOnDialog.close();
|
|
break;
|
|
default:
|
|
TurnBluetoothOnDialog.showConfirm().then((result) => {
|
|
if (result === 'confirm') {
|
|
// Turn Bluetooth on.
|
|
BtContext.setEnabled(true);
|
|
} else if (result === 'cancel') {
|
|
// Post error with reason then end the activity service.
|
|
this._endTransferWithReason('cancelled');
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Watch 'devicePicked', 'cancelSelection' event from device picker panel.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
*/
|
|
_watchEventFromDevicePicker: function bttm__watchEventFromDevicePicker() {
|
|
debug('_watchEventFromDevicePicker():');
|
|
// Watch 'devicePicked' event for reaching device picked from user.
|
|
DevicePickerPanel.addEventListener('devicePicked',
|
|
this._onDevicePicked.bind(this));
|
|
|
|
// Watch 'cancelSelection' event for reaching cancel selection from user.
|
|
DevicePickerPanel.addEventListener('cancelSelection',
|
|
this._onCancelSelection.bind(this));
|
|
},
|
|
|
|
/**
|
|
* While the device is picked from user, let's send file(s) to the device.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
* @param {Object} event
|
|
* @param {String} event.type - type of event name
|
|
* @param {Object} event.detail - device info in this object
|
|
* @param {Object} event.detail.address - address of device
|
|
* @returns {Promise}
|
|
*/
|
|
_onDevicePicked: function bttm__onDevicePicked(event) {
|
|
// Send file to the device via address
|
|
debug('_onDevicePicked(): address of target device = ' +
|
|
event.detail.address);
|
|
var targetDeviceAddress = event.detail.address;
|
|
var blobs = this._activity.source.data.blobs;
|
|
|
|
// Produce sending files schedule.
|
|
// Then, post message to system app for sending files in queue.
|
|
var schedule = this._produceSendingFilesSchedule(blobs.length);
|
|
this._postMessageToSystemApp(schedule);
|
|
|
|
// Send each file via Bluetooth sendFile API
|
|
return Promise.all(blobs.map((blob, index) => {
|
|
/**
|
|
* Check blob.name because sendFile() API needs a "file"
|
|
* object and a filaname before sending it.
|
|
* If there is no filename in the blob, Bluetooth API will give a
|
|
* default name "Unknown.jpeg".
|
|
* So Bluetooth app has to find out the name via device storage.
|
|
*/
|
|
if (blob.name) {
|
|
// The blob has name, send the blob directly.
|
|
debug('blob is sending with name...');
|
|
return BtContext.sendFile(targetDeviceAddress, blob);
|
|
} else if (this._activity.source.data.filepaths) {
|
|
// The blob does not have name,
|
|
// browse the file via filepath from storage again.
|
|
var filepath = this._activity.source.data.filepaths[index];
|
|
var storage = navigator.getDeviceStorage('sdcard');
|
|
return storage.get(filepath).then((file) => {
|
|
debug('getFile succeed & file is sending... file = ' + file);
|
|
return BtContext.sendFile(targetDeviceAddress, file);
|
|
}, (error) => {
|
|
debug('getFile failed so that blob is sending without filename ' +
|
|
error && error.name);
|
|
return BtContext.sendFile(targetDeviceAddress, blob);
|
|
});
|
|
} else {
|
|
// The blob does not have name and filepath,
|
|
// pass the blob without name to send file directly.
|
|
debug('no filepath to get from device storage ' +
|
|
'so that blob is sending without filename');
|
|
return BtContext.sendFile(targetDeviceAddress, blob);
|
|
}
|
|
})).then(() => {
|
|
debug('all files are already sent out..');
|
|
// Post result with reason then end the activity service.
|
|
this._endTransferWithReason('transferred');
|
|
return Promise.resolve();
|
|
});
|
|
},
|
|
|
|
/**
|
|
* While a user cancel selection, let's post result then end the activity
|
|
* service.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
* @param {Object} event
|
|
* @param {String} event.type - type of event name
|
|
*/
|
|
_onCancelSelection: function bttm__onCancelSelection(event) {
|
|
debug('_onCancelSelection(): cancel from user decision');
|
|
// Post error with reason then end the activity service.
|
|
this._endTransferWithReason('cancelled');
|
|
},
|
|
|
|
/**
|
|
* A function is a role producer.
|
|
* Bluetooth app produce one message for each sending file request.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
* @param {Number} numberOfTasks - number of tasks
|
|
* @returns {Object}
|
|
*/
|
|
_produceSendingFilesSchedule:
|
|
function bttm__produceSendingFilesSchedule(numberOfTasks) {
|
|
// Construct a object to contain info of sending files schedule.
|
|
// And the info will be posted message to system app.
|
|
// The result of send files will be displayed on notification.
|
|
var sendingFilesSchedule = {
|
|
numberOfFiles: numberOfTasks,
|
|
numSuccessful: 0,
|
|
numUnsuccessful: 0
|
|
};
|
|
return sendingFilesSchedule;
|
|
},
|
|
|
|
/**
|
|
* A function to do inner app communcation.
|
|
*
|
|
* @access private
|
|
* @memberOf TransferManager
|
|
* @param {Object} message - the info of sending files schedule
|
|
*/
|
|
_postMessageToSystemApp:
|
|
function bttm__postMessageToSystemApp(message) {
|
|
// Set up Inter-App Communications
|
|
var getRequest = navigator.mozApps.getSelf();
|
|
getRequest.onsuccess = (evt) => {
|
|
var app = evt.target.result;
|
|
// If IAC doesn't exist, just bail out.
|
|
if (!app.connect) {
|
|
debug('_postMessageToSystemApp(): Cannot post message since no ' +
|
|
'connect function, message = ' + JSON.stringify(message));
|
|
return;
|
|
}
|
|
// Post message object to system app.
|
|
app.connect('bluetoothTransfercomms').then((ports) => {
|
|
ports.forEach((port) => {
|
|
port.postMessage(message);
|
|
debug('_postMessageToSystemApp(): sending files schedule = ' +
|
|
JSON.stringify(message));
|
|
});
|
|
});
|
|
};
|
|
}
|
|
};
|
|
|
|
return TransferManager;
|
|
});
|