deviceSync.js
Go to the documentation of this file.
1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 :miv */
3 /*
4  *=BEGIN SONGBIRD GPL
5  *
6  * This file is part of the Songbird web player.
7  *
8  * Copyright(c) 2005-2011 POTI, Inc.
9  * http://www.songbirdnest.com
10  *
11  * This file may be licensed under the terms of of the
12  * GNU General Public License Version 2 (the ``GPL'').
13  *
14  * Software distributed under the License is distributed
15  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
16  * express or implied. See the GPL for the specific language
17  * governing rights and limitations.
18  *
19  * You should have received a copy of the GPL along with this
20  * program. If not, go to http://www.gnu.org/licenses/gpl.html
21  * or write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  *
24  *=END SONGBIRD GPL
25  */
26 
32 //------------------------------------------------------------------------------
33 //------------------------------------------------------------------------------
34 //
35 // Device sync widget.
36 //
37 //------------------------------------------------------------------------------
38 //------------------------------------------------------------------------------
39 
40 //------------------------------------------------------------------------------
41 //
42 // Device sync defs.
43 //
44 //------------------------------------------------------------------------------
45 
46 // Component manager defs.
47 if (typeof(Cc) == "undefined")
48  var Cc = Components.classes;
49 if (typeof(Ci) == "undefined")
50  var Ci = Components.interfaces;
51 if (typeof(Cr) == "undefined")
52  var Cr = Components.results;
53 if (typeof(Cu) == "undefined")
54  var Cu = Components.utils;
55 
56 Cu.import("resource://app/jsmodules/sbLibraryUtils.jsm");
57 Cu.import("resource://app/jsmodules/sbProperties.jsm");
58 Cu.import("resource://app/jsmodules/StringUtils.jsm");
59 
60 if (typeof(XUL_NS) == "undefined")
61  var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
62 
64  //
65  // Device sync object fields.
66  //
67  // _widget Device sync widget.
68  // _deviceLibrary Device library we are working with.
69  // _deviceSyncSettings Device sync settings we are working with.
70  // _mediaSyncSettings Device sync settings for this media type.
71  // _mediaType Media Type this sync widget represents.
72  // _ignoreDevicePrefChanges Flag to switch off device pref listener
73  // temporarily (to be used when we are changing the
74  // prefs ourselves).
75  //
76 
77  _widget: null,
78  _deviceLibrary: null,
79  _deviceSyncSettings: null,
80  _mediaSyncSettings: null,
81  _mediaType: null,
82  _ignoreDevicePrefChanges: false,
83 
91  initialize: function DeviceSyncWidget__initialize(aWidget) {
92  // Get the sync widget.
93  this._widget = aWidget;
94 
95  this._mediaType = this._widget.getAttribute("contenttype") || "audio";
96 
97  // Set up some variable UI labels (these depend on the contentType)
98  var syncAllLabel = this._getElement("content_auto_sync_all_radio");
99  syncAllLabel.label = SBString("device.sync.sync_all.label." +
100  this._mediaType);
101  var importHeaderCheckbox = this._getElement("import_header_checkbox");
102  importHeaderCheckbox.setAttribute("label",
103  SBString("device.import.header.label." +
104  this._mediaType));
105 
106  var syncHeaderCheckbox = this._getElement("sync_header_checkbox");
107  syncHeaderCheckbox.setAttribute("label",
108  SBString("device.sync.header.label." +
109  this._mediaType));
110 
111  // Initialize object fields.
112  this._device = this._widget.device;
113  this._deviceLibrary = this._widget.devLib;
114 
115  /* This initialize call can get made when a device is bound but the
116  * deviceLibrary hasn't been bound yet, so we need to prevent it from
117  * continuing and when the deviceLibrary is bound we'll get called again. */
118  if (!this._deviceLibrary) {
119  return;
120  }
121 
122  // Initialize settings and add listeners
123  this._deviceSyncSettings = this._deviceLibrary.syncSettings;
124  var mediaType = this._getMediaType(this._mediaType);
125  this._mediaSyncSettings = this._deviceSyncSettings
126  .getMediaSettings(mediaType);
127  this._deviceLibrary.addDeviceLibraryListener(this);
128 
129  // Listen for device events.
130  var deviceEventTarget =
131  this._device.QueryInterface(Ci.sbIDeviceEventTarget);
132  deviceEventTarget.addEventListener(this);
133 
134  // Listen for changes to playlists.
135  LibraryUtils.mainLibrary.addListener
136  (this,
137  false,
138  Ci.sbIMediaList.LISTENER_FLAGS_ITEMADDED |
139  Ci.sbIMediaList.LISTENER_FLAGS_AFTERITEMREMOVED |
140  Ci.sbIMediaList.LISTENER_FLAGS_ITEMUPDATED);
141 
142  // Update the UI.
143  this.update();
144  },
145 
146 
151  finalize: function DeviceSyncWidget_finalize() {
152  // Remove library listener.
153  LibraryUtils.mainLibrary.removeListener(this);
154 
155  // Remove the device libary listener for setting changes.
156  if (this._deviceLibrary)
157  this._deviceLibrary.removeDeviceLibraryListener(this);
158 
159  // Stop listening for device events.
160  if (this._device) {
161  var deviceEventTarget =
162  this._device.QueryInterface(Ci.sbIDeviceEventTarget);
163  deviceEventTarget.removeEventListener(this);
164  }
165 
166  // Finalize the device services.
167  this._deviceFinalize();
168 
169  // Clear object fields.
170  this._widget = null;
171  this._device = null;
172  },
173 
174 
179  update: function DeviceSyncWidget_update() {
180  // Ensure we have something to update
181  if (!this._widget || this._ignoreDevicePrefChanges)
182  return;
183 
184  // If there is no device library or mediaSyncSettings, hide the widget and
185  // return. Otherwise, show the widget.
186  if (!this._deviceLibrary || !this._mediaSyncSettings) {
187  this._widget.setAttribute("hidden", "true");
188  return;
189  }
190  this._widget.removeAttribute("hidden");
191 
192 
193  // If we are busy then disable the widget so the user can not make changes
194  if (this._device.isBusy) {
195  this._widget.setAttribute("disabled", true);
196  }
197  else {
198  this._widget.removeAttribute("disabled");
199  }
200 
201  // Show the video duration column only for the Video tab.
202  var syncPlaylistListVideoHeader =
203  this._getElement("content_auto_sync_playlist_duration");
204  syncPlaylistListVideoHeader.hidden = (this._mediaType != "video");
205 
206  // Set up the playlists
207  var syncPlaylistChildren = this._getElement("content_auto_sync_playlist_children");
208 
209  /* Clear the sync playlist tree. */
210  while (syncPlaylistChildren.firstChild)
211  syncPlaylistChildren.removeChild(syncPlaylistChildren.firstChild);
212 
213  // Get an nsIArray of available playlists for this media type
214  var syncPlayLists = this._mediaSyncSettings.syncPlaylists;
215  for (var listIndex = 0; listIndex < syncPlayLists.length; listIndex++) {
216  var mediaList = syncPlayLists.queryElementAt(listIndex, Ci.sbIMediaList);
217 
218  // Load up the information we need
219  var guid = mediaList.guid;
220 
221  // Duration only applies to video tab so don't waste time on other types
222  var duration = -1;
223  if (this._mediaType == "video")
224  duration = this._syncPrefsCalcDuration(mediaList);
225 
226  if (duration >= 0) {
227  var durationInfo =
228  Cc["@songbirdnest.com/Songbird/Properties/Info/Duration;1"]
229  .createInstance(Ci.sbIDurationPropertyInfo);
230  duration = durationInfo.format(duration);
231  }
232  else {
233  duration = SBString("device.sync.duration.unavailable");
234  }
235 
236  // Get the readable name of this list (add mix for mix lists)
237  var readableName = mediaList.name;
238  var listType = mediaList.getListContentType();
239  if (listType == Ci.sbIMediaList.CONTENTTYPE_MIX)
240  readableName = SBFormattedString("device.sync.mix." + this._mediaType,
241  [ readableName ]);
242 
243  /* Create our tree row */
244  var treeItem = document.createElementNS(XUL_NS, "treeitem");
245  var treeRow = document.createElementNS(XUL_NS, "treerow");
246  var treeCellCheck = document.createElementNS(XUL_NS, "treecell");
247  var treeCellTitle = document.createElementNS(XUL_NS, "treecell");
248  var treeCellDuration = document.createElementNS(XUL_NS, "treecell");
249 
250  /* Set the value of this row to the guid of the media list */
251  treeRow.value = guid;
252 
253  /* Setup the cells */
254  /* Check box (only editable cell) */
255  var isSelected = this._mediaSyncSettings.getPlaylistSelected(mediaList);
256  treeCellCheck.setAttribute("value", isSelected);
257  treeCellCheck.setAttribute("sbid",
258  "content_sync_playlist_checkcell." + guid);
259 
260  /* Title of the playlist */
261  treeCellTitle.setAttribute("label", readableName);
262  treeCellTitle.setAttribute("editable", "false");
263 
264  /* Duration of all the _mediaType conent in the playlist */
265  treeCellDuration.setAttribute("label", duration);
266  treeCellDuration.setAttribute("editable", "false");
267 
268  /* Append the cells to the row */
269  treeRow.appendChild(treeCellCheck);
270  treeRow.appendChild(treeCellTitle);
271  treeRow.appendChild(treeCellDuration);
272 
273  /* Append the row to the tree item */
274  treeItem.appendChild(treeRow);
275 
276  /* Add the row to the tree. */
277  syncPlaylistChildren.appendChild(treeItem);
278  }
279 
280  /* Get the management type pref UI elements. */
281  var syncRadioGroup = this._getElement("content_auto_sync_type_radio_group");
282  var syncPlaylistTree = this._getElement("content_auto_sync_playlist_tree");
283  var importEnabledCheckbox = this._getElement("import_header_checkbox");
284  var syncEnabledCheckbox = this._getElement("sync_header_checkbox");
285  var syncGroupbox = this._getElement("content_management_groupbox");
286 
287  /* Make the importEnabledCheckbox reflect whether this mediatype is
288  * currently set to be imported or not */
289  importEnabledCheckbox.checked = this._mediaSyncSettings.import;
290 
291  switch (this._mediaSyncSettings.mgmtType) {
292  case Ci.sbIDeviceLibraryMediaSyncSettings.SYNC_MGMT_NONE:
293  syncEnabledCheckbox.checked = false;
294  syncPlaylistTree.setAttribute("disabled", true);
295  syncRadioGroup.setAttribute("disabled", true);
296  syncGroupbox.setAttribute("disabled", true);
297  syncRadioGroup.selectedItem = null;
298  break;
299 
300  case Ci.sbIDeviceLibraryMediaSyncSettings.SYNC_MGMT_ALL:
301  syncEnabledCheckbox.checked = true;
302  syncPlaylistTree.setAttribute("disabled", true);
303  syncRadioGroup.removeAttribute("disabled");
304  syncGroupbox.removeAttribute("disabled");
305  syncRadioGroup.selectedItem = this._getElement
306  ("content_auto_sync_all_radio");
307  break;
308 
309  case Ci.sbIDeviceLibraryMediaSyncSettings.SYNC_MGMT_PLAYLISTS:
310  syncEnabledCheckbox.checked = true;
311  syncPlaylistTree.removeAttribute("disabled");
312  syncRadioGroup.removeAttribute("disabled");
313  syncGroupbox.removeAttribute("disabled");
314  syncRadioGroup.selectedItem = this._getElement
315  ("content_auto_sync_selected_radio");
316  break;
317  }
318 
319  },
320 
321 
322  //----------------------------------------------------------------------------
323  //
324  // Device sync event handler services.
325  //
326  //----------------------------------------------------------------------------
327 
332  onUIPrefChange: function DeviceSyncWidget_onUIPrefChange(aNeedUpdate) {
333  // Ignore user interaction if the widget is disabled.
334  if (this._widget.hasAttribute("disabled"))
335  return;
336 
337  // Ensure we do not update in the middle of updating the settings.
338  this._ignoreDevicePrefChanges = true;
339 
340  var syncRadioGroup = this._getElement("content_auto_sync_type_radio_group");
341  var importEnabledCheckbox = this._getElement("import_header_checkbox");
342  var syncEnabledCheckbox = this._getElement("sync_header_checkbox");
343 
344  // Activate or deactivate imporpy from device depending on checkbox setting
345  this._mediaSyncSettings.import = importEnabledCheckbox.checked;
346 
347  // First check the managementMode, we need to check the main check box and
348  // the radio group
349  var oldMgmtType = this._mediaSyncSettings.mgmtType;
350  var newMgmtType = Ci.sbIDeviceLibraryMediaSyncSettings.SYNC_MGMT_NONE;
351 
352  // If sync is enabled, figure out the mgmtType between playlists and all
353  if (syncEnabledCheckbox.checked) {
354  /* Video only does sync by playlist and if all or playlist sync is
355  * selected we can just go with that one. */
356  if ((this._mediaType == "video") ||
357  (syncRadioGroup.selectedItem ==
358  this._getElement("content_auto_sync_selected_radio"))) {
359  newMgmtType = Ci.sbIDeviceLibraryMediaSyncSettings.SYNC_MGMT_PLAYLISTS;
360  }
361  else if (syncRadioGroup.selectedItem ==
362  this._getElement("content_auto_sync_all_radio")) {
363  newMgmtType = Ci.sbIDeviceLibraryMediaSyncSettings.SYNC_MGMT_ALL;
364  }
365  else {
366  /* If nothing is selected then we set the mgmtType to the last
367  * not 'NONE' mgmtType used. This will default to ALL */
368  newMgmtType = this._mediaSyncSettings.lastActiveMgmtType;
369  }
370  }
371 
372  if (oldMgmtType != newMgmtType) {
373  this._mediaSyncSettings.mgmtType = newMgmtType;
374  }
375 
376  if (this._mediaSyncSettings.mgmtType ==
377  Ci.sbIDeviceLibraryMediaSyncSettings.SYNC_MGMT_PLAYLISTS) {
378  // Clear the playlists before we set them
379  this._mediaSyncSettings.clearSelectedPlaylists();
380 
381  // Now scan all the playlists in the list of playlists
382  var syncPlayLists = this._mediaSyncSettings.syncPlaylists;
383  for (var listIndex = 0; listIndex < syncPlayLists.length; listIndex++) {
384  var mediaList = syncPlayLists.queryElementAt(listIndex,
385  Ci.sbIMediaList);
386  if (mediaList) {
387  var guid = mediaList.guid;
388  var treeCellCheck = this._getElement(
389  "content_sync_playlist_checkcell." + guid);
390  if (treeCellCheck) {
391  // Get the new value
392  var isSelected = (treeCellCheck.getAttribute("value") == "true");
393  // First see if it is already set to the same value
394  var oldSelected = this._mediaSyncSettings
395  .getPlaylistSelected(mediaList);
396  // Now set it if changed
397  if (oldSelected != isSelected)
398  this._mediaSyncSettings.setPlaylistSelected(mediaList,
399  isSelected);
400  }
401  }
402  }
403  }
404 
405  /* Writes the new syncSettings to a device-specific pref so that they can be
406  * recalled between sessions */
407  this._deviceSyncSettings.write(this._device);
408 
409  this._ignoreDevicePrefChanges = false;
410  if (aNeedUpdate) {
411  // Finally update to ensure it all applied
412  this.update();
413  }
414  },
415 
416  //----------------------------------------------------------------------------
417  //
418  // sbIDeviceLibraryListener
419  //
420  //----------------------------------------------------------------------------
421 
422  // Implementation of sbIDeviceLibraryListener methods that must return true to
423  // prevent SB_NOTIFY_LISTENERS_ASK_PERMISSION in sbDeviceLibrary.cpp from
424  // aborting erroneously.
425 
426  onBeforeCreateMediaItem: function DeviceSyncWidget_onBeforeCreateMediaItem(
427  aContentUri,
428  aProperties,
429  aAllowDuplicates) {
430  return true;
431  },
432 
433  onBeforeCreateMediaList: function DeviceSyncWidget_onBeforeCreateMediaList(
434  aType,
435  aProperties) {
436  return true;
437  },
438 
439  onBeforeAdd: function DeviceSyncWidget_onBeforeAdd(aMediaItem) {
440  return true;
441  },
442 
443  onBeforeAddAll: function DeviceSyncWidget_onBeforeAddAll(aMediaList) {
444  return true;
445  },
446 
447  onBeforeAddSome: function DeviceSyncWidget_onBeforeAddSome(aMediaItems) {
448  return true;
449  },
450 
451  onBeforeClear: function DeviceSyncWidget_onBeforeClear() {
452  return true;
453  },
454 
455  //----------------------------------------------------------------------------
456  //
457  // Device sync sbIDeviceEventListener services.
458  //
459  //----------------------------------------------------------------------------
460 
467  onDeviceEvent: function DeviceSyncWidget_onDeviceEvent(aEvent) {
468  // Dispatch processing of the event.
469  switch(aEvent.type)
470  {
471  case Ci.sbIDeviceEvent.EVENT_DEVICE_PREFS_CHANGED:
472  case Ci.sbIDeviceEvent.EVENT_DEVICE_STATE_CHANGED:
473  case Ci.sbIDeviceEvent.EVENT_DEVICE_MOUNTING_END:
474  this.update();
475  break;
476 
477  default :
478  break;
479  }
480  },
481 
482 
483  //----------------------------------------------------------------------------
484  //
485  // Device sync sbIMediaListListener services.
486  //
487  //----------------------------------------------------------------------------
488 
498  onItemAdded: function DeviceSyncWidget_onItemAdded(aMediaList,
499  aMediaItem,
500  aIndex) {
501  // Handle unhidden playlist changes.
502  if (aMediaItem.getProperty(SBProperties.isList) &&
503  !aMediaItem.getProperty(SBProperties.hidden)) {
504  this.update();
505  }
506 
507  return false;
508  },
509 
510 
520  onBeforeItemRemoved: function DeviceSyncWidget_onBeforeItemRemoved
521  (aMediaList,
522  aMediaItem,
523  aIndex) {
524  return true;
525  },
526 
527 
537  onAfterItemRemoved: function DeviceSyncWidget_onAfterItemRemoved
538  (aMediaList,
539  aMediaItem,
540  aIndex) {
541  // Handle playlist changes.
542  if (aMediaItem.getProperty(SBProperties.isList))
543  this.update();
544 
545  return false;
546  },
547 
548 
560  onItemUpdated: function DeviceSyncWidget_onItemUpdated(aMediaList,
561  aMediaItem,
562  aProperties) {
563  // Handle playlist changes.
564  if (aMediaItem instanceof Ci.sbIMediaList)
565  this.update();
566 
567  return false;
568  },
569 
580  onBeforeListCleared:
581  function DeviceSyncWidget_onBeforeListCleared(aMediaList,
582  aExcludeLists) {
583  return true;
584  },
585 
596  onListCleared: function DeviceSyncWidget_onListCleared(aMediaList,
597  aExcludeLists) {
598  // Handle playlist changes.
599  if (aExcludeLists)
600  this.update();
601 
602  return false;
603  },
604 
605 
617  onBatchBegin: function DeviceSyncWidget_onBatchBegin(aMediaList) {},
618 
619 
631  onBatchEnd: function DeviceSyncWidget_onBatchEnd(aMediaList) {},
632 
633 
634  //----------------------------------------------------------------------------
635  //
636  // Device sync XUL services.
637  //
638  //----------------------------------------------------------------------------
639 
649  _getElement: function DeviceSyncWidget__getElement(aElementID) {
650  return document.getAnonymousElementByAttribute(this._widget,
651  "sbid",
652  aElementID);
653  },
654 
655  /*
656  * \brief get the ID of media type out of the media type string
657  *
658  * \param aMediaType media type string
659  *
660  */
661 
662  _getMediaType: function DeviceSyncWidget__getMediaType(aMediaType)
663  {
664  var mediaType = Ci.sbIDeviceLibrary.MEDIATYPE_UNKOWN;
665  if (aMediaType == "audio")
666  mediaType = Ci.sbIDeviceLibrary.MEDIATYPE_AUDIO;
667  else if (aMediaType == "video")
668  mediaType = Ci.sbIDeviceLibrary.MEDIATYPE_VIDEO;
669 
670  return mediaType;
671  },
672 
680  _syncPrefsCalcDuration:
681  function DeviceSyncWidget__syncPrefsCalcDuration(aMediaList) {
682  // sbIMediaListEnumerationListener
683  // This gives us a count and duration of items of contentType
684  var contentDuration = -1;
685  var durationCounter = {
686  onEnumerationBegin : function(aMediaList) {
687  return Ci.sbIMediaListEnumerationListener.CONTINUE;
688  },
689  onEnumeratedItem : function(aMediaList, aMediaItem) {
690  var duration = aMediaItem.getProperty(SBProperties.duration);
691  if (duration != null) {
692  contentDuration += parseFloat(duration);
693  }
694  return Ci.sbIMediaListEnumerationListener.CONTINUE;
695  },
696  onEnumerationEnd : function(aMediaList, aStatusCode) {
697  }
698  };
699 
700  aMediaList.enumerateItemsByProperty(SBProperties.contentType,
701  this._mediaType,
702  durationCounter,
703  Ci.sbIMediaList
704  .ENUMERATIONTYPE_SNAPSHOT);
705 
706  return contentDuration;
707  },
708 
709  //----------------------------------------------------------------------------
710  //
711  // Device sync device services.
712  //
713  // These services provide an interface to the device object.
714  // These services register for any pertinent device notifications and call
715  // _update to update the UI accordingly. In particular, these services
716  // register to receive notifications when the device state changes.
717  //
718  //----------------------------------------------------------------------------
719 
720  //
721  // Device info services fields.
722  //
723  // _device sbIDevice object.
724  //
725 
726  _device: null,
727 
732  _deviceFinalize: function DeviceSyncWidget__deviceFinalize() {
733  // Clear object fields.
734  this._device = null;
735  }
736 };
const Cu
function Fx prototype initialize
const Cc
const XUL_NS
Definition: FeedWriter.js:83
function SBFormattedString(aKey, aParams, aDefault, aStringBundle)
function SBString(aKey, aDefault, aStringBundle)
Definition: StringUtils.jsm:93
_hideDatepicker duration
var DeviceSyncWidget
Definition: deviceSync.js:63
return null
Definition: FeedWriter.js:1143
const Cr
const Ci
Javascript wrappers for common library tasks.