bunch of cleanup, reorganization, and get alarm/wakeup to work
This commit is contained in:
parent
6d133cfd72
commit
f8258c7a7e
13 changed files with 501 additions and 269 deletions
59
css/core.css
59
css/core.css
|
@ -13,8 +13,7 @@ body, .normalFont {
|
|||
|
||||
.main-container {
|
||||
height: 100%;
|
||||
height: -webkit-calc(100% - 30px);
|
||||
height: calc(100% - 30px);
|
||||
height: calc(100% - 42px);
|
||||
}
|
||||
|
||||
|
||||
|
@ -31,12 +30,16 @@ body, .normalFont {
|
|||
.topBar .topBarItem {
|
||||
padding: 6px;
|
||||
text-align: center;
|
||||
height: 18px;
|
||||
height: 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.topBar a, .topBar a:visited {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
.topBar .topBarButton {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
#controlButton {
|
||||
width: 50px;
|
||||
|
@ -59,27 +62,26 @@ body, .normalFont {
|
|||
.pageswitch-icon {
|
||||
transition-property: font-size, top, left, color;
|
||||
-webkit-transition-property: font-size, top, left, color;
|
||||
transition-duration: 1s;
|
||||
-webkit-transition-duration: 1s;
|
||||
position: absolute;
|
||||
}
|
||||
.pageswitch-icon.next {
|
||||
font-size: 18px;
|
||||
font-size: 30px;
|
||||
top: 5px;
|
||||
left: 20px;
|
||||
left: 16px;
|
||||
z-index: 10;
|
||||
}
|
||||
.pageswitch-icon.next1 {
|
||||
font-size: 12px;
|
||||
font-size: 20px;
|
||||
top: 12px;
|
||||
left: 33px;
|
||||
left: 40px;
|
||||
z-index: 8;
|
||||
color: #708090;
|
||||
}
|
||||
.pageswitch-icon.next2 {
|
||||
font-size: 10px;
|
||||
font-size: 18px;
|
||||
top: 3px;
|
||||
left: 33px;
|
||||
left: 40px;
|
||||
z-index: 6;
|
||||
color: #708090;
|
||||
}
|
||||
|
@ -124,7 +126,6 @@ body, .normalFont {
|
|||
.fullCurrentInfo {
|
||||
width: 100%;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
@ -171,6 +172,15 @@ body, .normalFont {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.newFeedField {
|
||||
width: calc(100% - 60px);
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
/* form */
|
||||
form.importForm {
|
||||
padding: 10px;
|
||||
|
@ -182,7 +192,6 @@ form.importForm {
|
|||
height: 25px;
|
||||
margin: 5px 0;
|
||||
-webkit-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
|
||||
-moz-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.importForm input[type="submit"] {
|
||||
|
@ -194,7 +203,6 @@ label {
|
|||
|
||||
|
||||
|
||||
|
||||
.feedData_tmp {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
|
@ -203,3 +211,28 @@ label {
|
|||
.feedImageThumbnail {
|
||||
width:40px;
|
||||
}
|
||||
|
||||
select {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
display: inline-block;
|
||||
width: 210px;
|
||||
padding: 4px;
|
||||
margin-bottom: 9px;
|
||||
font-size: 13px;
|
||||
color: #555555;
|
||||
border: 1px solid #ccc;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
|
||||
height: 28px;
|
||||
/* In IE7, the height of the select element cannot be changed by height, only font-size */
|
||||
margin-top: 4px;
|
||||
/* For IE7, add top margin to align select with labels */
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: red;
|
||||
}
|
|
@ -17,9 +17,9 @@
|
|||
<i id="pageswitch-icon-queue" class="pageswitch-icon next2 icon-th-list"></i>
|
||||
<i id="pageswitch-icon-info" class="pageswitch-icon icon-info-sign" ng-show="showInfoIcon"></i>
|
||||
</div>
|
||||
<div id="controlButton" class="topBarItem" ng-click="playPause()">
|
||||
<i class="icon-play"></i>
|
||||
<i class="icon-pause"></i>
|
||||
<div id="controlButton" class="topBarItem topBarButton" ng-click="playPause()">
|
||||
<i class="icon-play" ng-class=""></i>
|
||||
<i class="icon-pause" ng-class=""></i>
|
||||
</div>
|
||||
<div ng-click="currentInfo()" class="topBarItem">
|
||||
<span>{{ nowPlaying.title }}</span>
|
||||
|
@ -30,7 +30,6 @@
|
|||
<div class="fullCurrentInfo topBarMenu" ng-show="showingFullCurrentInfo" ng-cloak>
|
||||
{{ nowPlaying.description }}
|
||||
{{ nowPlaying.date|date }}
|
||||
more information about song playing (description, link to go to this feed, more controls?)
|
||||
</div>
|
||||
<div class="pageSwitchMenu topBarMenu" ng-show="showingPageSwitchMenu" ng-cloak>
|
||||
<ul>
|
||||
|
@ -48,6 +47,7 @@
|
|||
|
||||
<script src="lib/angular/angular.js"></script>
|
||||
<script src="lib/iscroll.js"></script>
|
||||
<script src="js/alarmManagers.js"></script>
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/utilities.js"></script>
|
||||
<script src="js/services.js"></script>
|
||||
|
|
107
js/alarmManagers.js
Normal file
107
js/alarmManagers.js
Normal file
|
@ -0,0 +1,107 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('podcasts.alarmManager', ['podcasts.settings', 'podcasts.downloader'])
|
||||
.run(['alarmManager', 'updateFeedsAlarmManager', function(alarmManager, updateFeedsAlarmManager) {
|
||||
updateFeedsAlarmManager.setAlarmListener();
|
||||
alarmManager.setAlarmListener();
|
||||
}])
|
||||
.service('alarmManager', function() {
|
||||
var alarmManager = navigator.mozAlarms,
|
||||
alarmHandlers = [];
|
||||
|
||||
if (!alarmManager) {
|
||||
console.log('navigator.mozAlarms is not available');
|
||||
return {
|
||||
setAlarmIn: angular.noop,
|
||||
removeExistingAlarms: angular.noop,
|
||||
setAlarmListener: angular.noop,
|
||||
addAlarmListener: angular.noop
|
||||
};
|
||||
}
|
||||
|
||||
function setAlarmIn(milliSeconds, data) {
|
||||
var now = new Date(),
|
||||
alarmDate = new Date(+now + +milliSeconds);
|
||||
|
||||
//TODO: check how to set timezone-specific alarms
|
||||
var setAlarmRequest = alarmManager.add(alarmDate, "ignoreTimezone", data);
|
||||
setAlarmRequest.onsuccess = function () {
|
||||
console.log("Alarm scheduled for " + alarmDate);
|
||||
};
|
||||
setAlarmRequest.onerror = function () {
|
||||
console.log("An error occurred when scheduling the alarm: " + e.target.error.name);
|
||||
};
|
||||
}
|
||||
|
||||
function removeExistingAlarms(type)
|
||||
{
|
||||
var allAlarms = alarmManager.getAll();
|
||||
allAlarms.onsuccess = function (e) {
|
||||
this.result.forEach(function (alarm) {
|
||||
if (type === alarm.data.type) {
|
||||
alarmManager.remove(alarm.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function addAlarmListener(type, alarmFunction)
|
||||
{
|
||||
alarmHandlers.push({type: type, handle: alarmFunction});
|
||||
}
|
||||
|
||||
function handleAlarm(alarm)
|
||||
{
|
||||
alarmHandlers.forEach(function (alarmHandler) {
|
||||
if (alarm.data.type == alarmHandler.type) {
|
||||
alarmHandler.handle(alarm);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setAlarmListener()
|
||||
{
|
||||
navigator.mozSetMessageHandler("alarm", handleAlarm);
|
||||
}
|
||||
|
||||
return {
|
||||
setAlarmIn: setAlarmIn,
|
||||
removeExistingAlarms: removeExistingAlarms,
|
||||
addAlarmListener: addAlarmListener,
|
||||
setAlarmListener: setAlarmListener
|
||||
};
|
||||
})
|
||||
.service('updateFeedsAlarmManager', ['settings', 'alarmManager', 'downloader', function(settings, alarmManager, downloader) {
|
||||
var alarmType = "updateFeeds";
|
||||
|
||||
function setAlarm()
|
||||
{
|
||||
settings.get('refreshInterval', function(value) {
|
||||
if (value.value > 0) {
|
||||
var refreshInterval = value.value;
|
||||
|
||||
alarmManager.setAlarmIn(refreshInterval, {type: alarmType});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function changeAlarmInterval(newInterval)
|
||||
{
|
||||
alarmManager.removeExistingAlarms(alarmType);
|
||||
this.setAlarm();
|
||||
}
|
||||
|
||||
function setAlarmListener()
|
||||
{
|
||||
alarmManager.addAlarmListener(alarmType, function(alarm) {
|
||||
downloader.downloadAll(true);
|
||||
setAlarm();
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
setAlarm: setAlarm,
|
||||
changeAlarmInterval: changeAlarmInterval,
|
||||
setAlarmListener: setAlarmListener
|
||||
};
|
||||
}]);
|
|
@ -9,5 +9,6 @@ angular.module('podcasts', ['podcasts.services', 'podcasts.updater', 'podcasts.d
|
|||
$routeProvider.when('/settings', {templateUrl: 'partials/settings.html', controller: SettingsCtrl});
|
||||
$routeProvider.when('/import/google', {templateUrl: 'partials/importGoogle.html', controller: ImportCtrl});
|
||||
$routeProvider.when('/info', {templateUrl: 'partials/info.html', controller: InfoCtrl});
|
||||
$routeProvider.when('/dev', {templateUrl: 'partials/dev.html', controller: DevCtrl});
|
||||
$routeProvider.otherwise({redirectTo: '/queue'});
|
||||
}]);
|
|
@ -9,6 +9,18 @@ function FeedListCtrl($scope, feeds, pageSwitcher, $location) {
|
|||
feeds.add($scope.newFeedUrl);
|
||||
};
|
||||
|
||||
$scope.preFillField = function() {
|
||||
if (angular.isUndefined($scope.newFeedUrl) || $scope.newFeedUrl == "") {
|
||||
$scope.newFeedUrl = "http://";
|
||||
}
|
||||
};
|
||||
|
||||
$scope.removePrefillIfNecessary = function() {
|
||||
if ($scope.newFeedUrl == "http://") {
|
||||
$scope.newFeedUrl = "";
|
||||
}
|
||||
};
|
||||
|
||||
$scope.goToFeed = function(hash) {
|
||||
$location.path('/feed/'+hash);
|
||||
};
|
||||
|
@ -16,47 +28,6 @@ function FeedListCtrl($scope, feeds, pageSwitcher, $location) {
|
|||
pageSwitcher.change('feeds');
|
||||
}
|
||||
|
||||
function TopLinksCtrl($scope, downloader, google) {
|
||||
$scope.downloadFiles = function() {
|
||||
downloader.downloadAll();
|
||||
};
|
||||
|
||||
$scope.loadFixtures = function() {
|
||||
var newFeed = {};
|
||||
newFeed.title = "Some OGG Vorbis Podcast";
|
||||
newFeed.url = "http://www.c3d2.de/pentacast-ogg.xml";
|
||||
|
||||
//add new record to the local database
|
||||
ixDbEz.put("feed", newFeed);
|
||||
|
||||
|
||||
var newFeedItem = {};
|
||||
newFeedItem.guid = 'http://example.com/1';
|
||||
newFeedItem.feedId = 1;
|
||||
newFeedItem.title = 'Example Item 1';
|
||||
newFeedItem.link = 'http://example.com/1';
|
||||
newFeedItem.date = 'Date';
|
||||
newFeedItem.description = 'Long Description<br /> with HTML <b>and stuff</b>';
|
||||
newFeedItem.audioUrl = 'http://example.com/1';
|
||||
newFeedItem.queued = 1;
|
||||
|
||||
ixDbEz.put("feedItem", newFeedItem);
|
||||
|
||||
|
||||
var newFeedItem = {};
|
||||
newFeedItem.guid = 'http://example.com/2';
|
||||
newFeedItem.feedId = 1;
|
||||
newFeedItem.title = 'Example Item 2';
|
||||
newFeedItem.link = 'http://example.com/2';
|
||||
newFeedItem.date = 'Date';
|
||||
newFeedItem.description = 'Second Long Description<br /> with HTML <b>and stuff</b>';
|
||||
newFeedItem.audioUrl = 'http://example.com/2';
|
||||
newFeedItem.queued = 1;
|
||||
|
||||
ixDbEz.put("feedItem", newFeedItem);
|
||||
};
|
||||
}
|
||||
|
||||
function FeedCtrl($scope, $routeParams, $location, feeds, pageSwitcher) {
|
||||
$scope.nrQueueItemsOptions = [1, 2, 3, 4, 5];
|
||||
$scope.feed = {};
|
||||
|
@ -101,7 +72,7 @@ function QueueListCtrl($scope, $rootScope, pageSwitcher, feedItems, feeds, queue
|
|||
pageSwitcher.change('queue');
|
||||
}
|
||||
|
||||
function SettingsCtrl($scope, settings, pageSwitcher) {
|
||||
function SettingsCtrl($scope, settings, pageSwitcher, updateFeedsAlarmManager) {
|
||||
$scope.refreshIntervalOptions = [
|
||||
{name: '10 seconds', value: '10000'},
|
||||
{name: '1 hour', value: '3600000'},
|
||||
|
@ -112,11 +83,17 @@ function SettingsCtrl($scope, settings, pageSwitcher) {
|
|||
$scope.settings = {};
|
||||
|
||||
$scope.changeInterval = function() {
|
||||
settings.set('refreshInterval', $scope.refreshInterval.value, $scope.refreshInterval.id);
|
||||
settings.set(
|
||||
'refreshInterval',
|
||||
$scope.settings.refreshInterval.value,
|
||||
$scope.settings.refreshInterval.id
|
||||
);
|
||||
|
||||
updateFeedsAlarmManager.changeAlarmInterval();
|
||||
};
|
||||
|
||||
$scope.changeDownloadOnWifi = function() {
|
||||
settings.set('downloadOnWifi', $scope.downloadOnWifi.value, $scope.downloadOnWifi.id);
|
||||
settings.set('downloadOnWifi', $scope.settings.downloadOnWifi.value, $scope.settings.downloadOnWifi.id);
|
||||
};
|
||||
|
||||
settings.setAllValuesInScope($scope);
|
||||
|
@ -131,6 +108,7 @@ function TopBarCtrl($scope, player, pageSwitcher)
|
|||
$scope.showingPageSwitchMenu = false;
|
||||
$scope.showingFullCurrentInfo = false;
|
||||
$scope.showInfoIcon = false;
|
||||
$scope.playing = player.playing;
|
||||
|
||||
$scope.playPause = function() {
|
||||
if (player.playing()) {
|
||||
|
@ -182,4 +160,21 @@ function ImportCtrl($scope, pageSwitcher, google)
|
|||
|
||||
pageSwitcher.backPage = true;
|
||||
pageSwitcher.change('settings');
|
||||
}
|
||||
|
||||
|
||||
|
||||
function DevCtrl($scope, downloader, updateFeedsAlarmManager)
|
||||
{
|
||||
$scope.downloadFiles = function() {
|
||||
downloader.downloadAll();
|
||||
};
|
||||
|
||||
$scope.checkForPendingMessage = function() {
|
||||
console.log(navigator.mozHasPendingMessage('alarm'));
|
||||
};
|
||||
|
||||
$scope.setAlarmTmp = function() {
|
||||
updateFeedsAlarmManager.setAlarm();
|
||||
};
|
||||
}
|
|
@ -123,4 +123,24 @@ angular.module('podcast.directives', [])
|
|||
}
|
||||
};
|
||||
})
|
||||
.directive('ngFocus', ['$parse', function($parse) {
|
||||
return function(scope, element, attr) {
|
||||
var fn = $parse(attr['ngFocus']);
|
||||
element.bind('focus', function(event) {
|
||||
scope.$apply(function() {
|
||||
fn(scope, {$event:event});
|
||||
});
|
||||
});
|
||||
}
|
||||
}])
|
||||
.directive('ngBlur', ['$parse', function($parse) {
|
||||
return function(scope, element, attr) {
|
||||
var fn = $parse(attr['ngBlur']);
|
||||
element.bind('blur', function(event) {
|
||||
scope.$apply(function() {
|
||||
fn(scope, {$event:event});
|
||||
});
|
||||
});
|
||||
}
|
||||
}])
|
||||
;
|
440
js/services.js
440
js/services.js
|
@ -2,38 +2,6 @@
|
|||
|
||||
/* Services */
|
||||
angular.module('podcasts.services', ['podcasts.utilities'])
|
||||
.service('downloaderBackend', ['$http', '$q', 'xmlParser', '$rootScope', function($http, $q, xmlParser, $rootScope) {
|
||||
return {
|
||||
downloadFile: function(url) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
$http.get(url, {'responseType': 'blob'})
|
||||
.success(function(file) {
|
||||
deferred.resolve(file);
|
||||
})
|
||||
.error(function() {
|
||||
deferred.reject();
|
||||
})
|
||||
;
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
downloadXml: function(url) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
$rootScope.$apply($http.get(url)
|
||||
.success(function(xml) {
|
||||
deferred.resolve(xmlParser.parse(xml));
|
||||
})
|
||||
.error(function(data, status, headers, config) {
|
||||
deferred.reject();
|
||||
})
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
}
|
||||
}])
|
||||
.value('queueList', {
|
||||
queue: [],
|
||||
scope: null,
|
||||
|
@ -130,7 +98,10 @@ angular.module('podcasts.services', ['podcasts.utilities'])
|
|||
ixDbCursorReq.onsuccess = function (e) {
|
||||
var cursor = ixDbCursorReq.result || e.result;
|
||||
if (cursor) {
|
||||
queueList.addToQueue(cursor.value);
|
||||
// This additional check is necessary, since the index doesn't seem to always catch correctly
|
||||
if (cursor.value.queued) {
|
||||
queueList.addToQueue(cursor.value);
|
||||
}
|
||||
|
||||
cursor.continue();
|
||||
} else {
|
||||
|
@ -140,7 +111,7 @@ angular.module('podcasts.services', ['podcasts.utilities'])
|
|||
}
|
||||
}
|
||||
}
|
||||
}, undefined, IDBKeyRange.only(1), undefined, 'ixQueued');
|
||||
}, undefined, IDBKeyRange.only(1), false, 'ixQueued');
|
||||
}
|
||||
}
|
||||
}])
|
||||
|
@ -284,6 +255,7 @@ angular.module('podcasts.services', ['podcasts.utilities'])
|
|||
downloadItems: function(feedItem, updateStatus) {
|
||||
var promise = downloaderBackend.downloadXml(feedItem.url),
|
||||
feedObjects = [];
|
||||
|
||||
promise.then(function(data) {
|
||||
angular.forEach(
|
||||
data.find('item'),
|
||||
|
@ -324,86 +296,117 @@ angular.module('podcasts.services', ['podcasts.utilities'])
|
|||
return {
|
||||
url: $window.URL || $window.webkitURL,
|
||||
createObjectUrl: function(data) {
|
||||
return this.url.createObjectUrl(data);
|
||||
return this.url.createObjectURL(data);
|
||||
}
|
||||
};
|
||||
}])
|
||||
.service('player', ['db', 'url', '$timeout', function(db, url, $timeout) {
|
||||
var audioSetup = new Audio();
|
||||
audioSetup.setAttribute("mozaudiochannel", "content");
|
||||
var audio = new Audio();
|
||||
audio.setAttribute("mozaudiochannel", "content");
|
||||
var currentFeedItem = null;
|
||||
var nowPlaying = {position: 0, duration: 0, title: '', description: '', feed: '', date: 0};
|
||||
|
||||
var acm = navigator.mozAudioChannelManager;
|
||||
|
||||
if (acm) {
|
||||
acm.addEventListener('headphoneschange', function onheadphoneschange() {
|
||||
if (!acm.headphones && PlayerView.isPlaying) {
|
||||
PlayerView.pause();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function play(feedItem, $scope)
|
||||
{
|
||||
if (feedItem) {
|
||||
currentFeedItem = feedItem;
|
||||
var audioSrc;
|
||||
|
||||
if (feedItem.audio) {
|
||||
console.log('Playing audio from download');
|
||||
audioSrc = url.createObjectUrl(feedItem.audio);
|
||||
} else {
|
||||
console.log('Playing audio from web');
|
||||
audioSrc = feedItem.audioUrl;
|
||||
}
|
||||
|
||||
audio.src = audioSrc;
|
||||
updateSong(feedItem, $scope);
|
||||
|
||||
if (feedItem.position) {
|
||||
angular.element(audio).bind('canplay', function(event) {
|
||||
this.currentTime = feedItem.position;
|
||||
|
||||
angular.element(this).unbind('canplay');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
audio.play();
|
||||
|
||||
//TODO: handle save when feedItem is not passed in
|
||||
angular.element(audio).bind('pause', function(event) {
|
||||
feedItem.position = Math.floor(event.target.currentTime);
|
||||
db.put("feedItem", feedItem);
|
||||
|
||||
angular.element(this).unbind();
|
||||
});
|
||||
|
||||
// TODO: add something here for when audio is done to remove from queue and go to next song
|
||||
angular.element(audio).bind('ended', function(event) {
|
||||
feedItem.queued = 0;
|
||||
feedItem.position = 0;
|
||||
db.put("feedItem", feedItem);
|
||||
|
||||
// start next item
|
||||
// get next item from queue
|
||||
play(nextFeedItem, $scope);
|
||||
|
||||
angular.element(this).unbind();
|
||||
});
|
||||
}
|
||||
|
||||
function pause()
|
||||
{
|
||||
audio.pause();
|
||||
}
|
||||
|
||||
function playing()
|
||||
{
|
||||
return !audio.paused;
|
||||
}
|
||||
|
||||
function updateSong(feedItem, $scope)
|
||||
{
|
||||
nowPlaying.title = feedItem.title;
|
||||
/*$timeout(function() {
|
||||
player.nowPlaying.duration = audio.duration;
|
||||
}, 100);*/
|
||||
nowPlaying.currentFeedItem = feedItem;
|
||||
nowPlaying.description = feedItem.description;
|
||||
nowPlaying.feed = feedItem.feed;
|
||||
nowPlaying.date = feedItem.date;
|
||||
updatePosition($scope);
|
||||
}
|
||||
|
||||
function updatePosition($scope)
|
||||
{
|
||||
setInterval(function() {
|
||||
nowPlaying.position = audio.currentTime;
|
||||
$scope.$apply();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
db: db,
|
||||
audio: audioSetup,
|
||||
feedItem: null,
|
||||
nowPlaying: {position: 0, duration: 0, title: '', description: '', feed: '', date: 0},
|
||||
play: function (feedItem, $scope) {
|
||||
if (feedItem) {
|
||||
this.feedItem = feedItem;
|
||||
|
||||
var audioSrc;
|
||||
|
||||
if (feedItem.audio) {
|
||||
audioSrc = url.createObjectUrl(feedItem.audio);
|
||||
} else {
|
||||
audioSrc = feedItem.audioUrl;
|
||||
}
|
||||
|
||||
this.audio.src = audioSrc;
|
||||
this.updateSong(feedItem, $scope);
|
||||
|
||||
if (feedItem.position) {
|
||||
angular.element(this.audio).bind('canplay', function(event) {
|
||||
this.currentTime = feedItem.position;
|
||||
});
|
||||
}
|
||||
}
|
||||
this.audio.play();
|
||||
|
||||
var db = this.db;
|
||||
angular.element(this.audio).bind('pause', function(event) {
|
||||
feedItem.position = Math.floor(event.target.currentTime);
|
||||
db.put("feedItem", feedItem);
|
||||
});
|
||||
|
||||
// TODO: add something here for when audio is done to remove from queue and go to next song
|
||||
angular.element(this.audio).bind('ended', function(event) {
|
||||
feedItem.queued = 0;
|
||||
feedItem.position = 0;
|
||||
db.put("feedItem", feedItem);
|
||||
|
||||
// start next item
|
||||
// get next item from queue
|
||||
play(nextFeedItem, $scope);
|
||||
});
|
||||
},
|
||||
pause: function() {
|
||||
this.audio.pause();
|
||||
},
|
||||
playing: function() {
|
||||
return !this.audio.paused;
|
||||
},
|
||||
updateSong: function(feedItem, $scope) {
|
||||
this.nowPlaying.title = feedItem.title;
|
||||
var audio = this.audio,
|
||||
player = this;
|
||||
$timeout(function() {
|
||||
player.nowPlaying.duration = audio.duration;
|
||||
}, 100);
|
||||
this.nowPlaying.feedItem = feedItem;
|
||||
this.nowPlaying.description = feedItem.description;
|
||||
this.nowPlaying.feed = feedItem.feed;
|
||||
this.nowPlaying.date = feedItem.date;
|
||||
this.updatePosition($scope);
|
||||
},
|
||||
updatePosition: function($scope) {
|
||||
var audio = this.audio,
|
||||
player = this;
|
||||
setInterval(function() {
|
||||
player.nowPlaying.position = audio.currentTime;
|
||||
$scope.$apply();
|
||||
}, 1000);
|
||||
}
|
||||
audio: audio,
|
||||
feedItem: currentFeedItem,
|
||||
nowPlaying: nowPlaying,
|
||||
play: play,
|
||||
pause: pause,
|
||||
playing: playing,
|
||||
updateSong: updateSong,
|
||||
updatePosition: updatePosition
|
||||
}
|
||||
}])
|
||||
.service('pageSwitcher', ['$location', '$route', function($location, $route) {
|
||||
|
@ -471,77 +474,6 @@ angular.module('podcasts.services', ['podcasts.utilities'])
|
|||
}
|
||||
}
|
||||
}])
|
||||
.service('downloader', ['db', 'url', '$http', 'settings', function(db, url, $http, settings) {
|
||||
return {
|
||||
allowedToDownload: function(callback) {
|
||||
settings.get('downloadOnWifi', function(setting) {
|
||||
if (setting.value) {
|
||||
//TODO: check if we're on wifi
|
||||
callback(false);
|
||||
} else {
|
||||
callback(true);
|
||||
}
|
||||
}, function() {
|
||||
callback(true); // Default value is "allowed" - maybe change this?
|
||||
});
|
||||
},
|
||||
downloadAll: function(silent) {
|
||||
var downloader = this;
|
||||
this.allowedToDownload(function(value) {
|
||||
if (!value) {
|
||||
if (typeof silent !== 'undefined' && !silent) {
|
||||
alert('not Downloading because not on WiFi'); //TODO: nicer error message?
|
||||
}
|
||||
} else {
|
||||
var itemsToDownload = [];
|
||||
db.getCursor("feedItem", function(ixDbCursorReq)
|
||||
{
|
||||
if(typeof ixDbCursorReq !== "undefined") {
|
||||
ixDbCursorReq.onsuccess = function (e) {
|
||||
var cursor = ixDbCursorReq.result || e.result;
|
||||
|
||||
if (cursor) {
|
||||
if (!cursor.value.audio && cursor.value.audioUrl) {
|
||||
itemsToDownload.push(cursor.value);
|
||||
}
|
||||
cursor.continue();
|
||||
} else {
|
||||
downloader.downloadFiles(itemsToDownload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, undefined, IDBKeyRange.only(1), undefined, 'ixQueued');
|
||||
}
|
||||
});
|
||||
},
|
||||
downloadFiles: function(itemsToDownload) {
|
||||
var item = itemsToDownload.shift(),
|
||||
downloader = this;
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
$http.get(item.audioUrl, {'responseType': 'blob'})
|
||||
.success(function(data) {
|
||||
item.audio = data;
|
||||
item.duration = this.getAudioLength(data);
|
||||
|
||||
db.put("feedItem", item);
|
||||
|
||||
downloader.downloadFiles(itemsToDownload);
|
||||
})
|
||||
;
|
||||
},
|
||||
getAudioLength: function(audio) {
|
||||
var tmpAudio = new Audio();
|
||||
tmpAudio.autoplay = false;
|
||||
tmpAudio.muted = true;
|
||||
tmpAudio.src = url.createObjectURL(audio);
|
||||
|
||||
return tmpAudio.duration;
|
||||
}
|
||||
};
|
||||
}])
|
||||
.filter('time', function() {
|
||||
return function(input, skip) {
|
||||
var seconds, minutes, hours;
|
||||
|
@ -592,7 +524,124 @@ angular.module('podcasts.services', ['podcasts.utilities'])
|
|||
day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago" ||
|
||||
"older than a month";
|
||||
}
|
||||
});
|
||||
})
|
||||
.service('downloaderBackend', ['$http', '$q', 'xmlParser', '$rootScope', function($http, $q, xmlParser, $rootScope) {
|
||||
return {
|
||||
downloadFile: function(url) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
$http.get(url, {'responseType': 'blob'})
|
||||
.success(function(file) {
|
||||
deferred.resolve(file);
|
||||
})
|
||||
.error(function() {
|
||||
deferred.reject();
|
||||
})
|
||||
;
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
downloadXml: function(url) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
$rootScope.$apply($http.get(url)
|
||||
.success(function(xml) {
|
||||
deferred.resolve(xmlParser.parse(xml));
|
||||
})
|
||||
.error(function(data, status, headers, config) {
|
||||
deferred.reject();
|
||||
})
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
||||
angular.module('podcasts.downloader', ['podcasts.settings', 'podcasts.database', 'podcasts.utilities'])
|
||||
.service('downloader', ['db', 'url', '$http', 'settings', '$rootScope', function(db, url, $http, settings, $rootScope) {
|
||||
return {
|
||||
allowedToDownload: function(callback) {
|
||||
callback(true);
|
||||
|
||||
/*
|
||||
Not sure how to check this...
|
||||
settings.get('downloadOnWifi', function(setting) {
|
||||
if (setting.value) {
|
||||
//TODO: check if we're on wifi
|
||||
callback(false);
|
||||
} else {
|
||||
callback(true);
|
||||
}
|
||||
}, function() {
|
||||
callback(true); // Default value is "allowed" - maybe change this?
|
||||
});
|
||||
*/
|
||||
},
|
||||
downloadAll: function(silent) {
|
||||
var downloader = this;
|
||||
this.allowedToDownload(function(value) {
|
||||
if (!value) {
|
||||
console.log('Not Allowed to Download because not on Wifi');
|
||||
if (!angular.isUndefined(silent) && !silent) {
|
||||
alert('not Downloading because not on WiFi'); //TODO: nicer error message?
|
||||
}
|
||||
} else {
|
||||
var itemsToDownload = [];
|
||||
db.getCursor("feedItem", function(ixDbCursorReq)
|
||||
{
|
||||
if(typeof ixDbCursorReq !== "undefined") {
|
||||
ixDbCursorReq.onsuccess = function (e) {
|
||||
var cursor = ixDbCursorReq.result || e.result;
|
||||
|
||||
if (cursor) {
|
||||
if (!cursor.value.audio && cursor.value.audioUrl) {
|
||||
itemsToDownload.push(cursor.value);
|
||||
}
|
||||
cursor.continue();
|
||||
} else {
|
||||
downloader.downloadFiles(itemsToDownload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, undefined, IDBKeyRange.only(1), undefined, 'ixQueued');
|
||||
}
|
||||
});
|
||||
},
|
||||
downloadFiles: function(itemsToDownload) {
|
||||
var item = itemsToDownload.shift(),
|
||||
downloader = this;
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rootScope.$apply(
|
||||
$http.get(item.audioUrl, {'responseType': 'blob'})
|
||||
.success(function(data) {
|
||||
console.log('downloaded audio file for saving');
|
||||
|
||||
item.audio = data;
|
||||
item.duration = downloader.getAudioLength(data);
|
||||
|
||||
db.put("feedItem", item);
|
||||
|
||||
downloader.downloadFiles(itemsToDownload);
|
||||
})
|
||||
.error(function() {
|
||||
console.warn('Could not download file');
|
||||
})
|
||||
);
|
||||
},
|
||||
getAudioLength: function(audio) {
|
||||
var tmpAudio = new Audio();
|
||||
tmpAudio.autoplay = false;
|
||||
tmpAudio.muted = true;
|
||||
tmpAudio.src = url.createObjectUrl(audio);
|
||||
|
||||
return tmpAudio.duration;
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
angular.module('podcasts.database', [])
|
||||
.run(function() {
|
||||
|
@ -606,10 +655,11 @@ angular.module('podcasts.database', [])
|
|||
ixDbEz.createIndex("feedItem", "ixQueued", "queued");
|
||||
ixDbEz.createObjStore("setting", "id", true);
|
||||
ixDbEz.createIndex("setting", "ixName", "name", true);
|
||||
ixDbEz.put("setting", {'name': "refreshInterval", 'value': 20000});
|
||||
});
|
||||
|
||||
//Create or Open the local IndexedDB database via ixDbEz
|
||||
ixDbEz.startDB("podcastDb", 8, dbConfig, undefined, undefined, false);
|
||||
ixDbEz.startDB("podcastDb", 10, dbConfig, undefined, undefined, false);
|
||||
})
|
||||
.value('db', ixDbEz)
|
||||
.service('dbNew', ['$q', '$rootScope', 'db', function($q, $rootScope, _db) {
|
||||
|
@ -645,25 +695,17 @@ angular.module('podcasts.database', [])
|
|||
};
|
||||
}]);
|
||||
|
||||
angular.module('podcasts.updater', ['podcasts.settings'])
|
||||
|
||||
angular.module('podcasts.updater', ['podcasts.settings', 'podcasts.alarmManager', 'podcasts.downloader'])
|
||||
.run(['update', function(update) {
|
||||
update.checkFeeds();
|
||||
//update.checkFeeds();
|
||||
}])
|
||||
.service('update', ['$timeout', '$log', 'settings', 'downloader', function($timeout, $log, settings, downloader) {
|
||||
.service('update', ['$log', 'downloader', 'updateFeedsAlarmManager', function($log, downloader, updateFeedsAlarmManager) {
|
||||
var checkFeeds = function() {
|
||||
$log.info('Running Feed Check');
|
||||
|
||||
settings.get('refreshInterval', function(value) {
|
||||
if (value.value > 0) {
|
||||
var refreshInterval = value.value;
|
||||
|
||||
update();
|
||||
|
||||
if (refreshInterval > 0) {
|
||||
$timeout(checkFeeds, refreshInterval);
|
||||
}
|
||||
}
|
||||
});
|
||||
update();
|
||||
updateFeedsAlarmManager.setAlarm();
|
||||
};
|
||||
|
||||
function update() {
|
||||
|
@ -687,7 +729,6 @@ angular.module('podcasts.settings', ['podcasts.database'])
|
|||
waiting = [];
|
||||
|
||||
function _init() {
|
||||
console.log('init settings');
|
||||
db.getCursor("setting", function(ixDbCursorReq)
|
||||
{
|
||||
if(typeof ixDbCursorReq !== "undefined") {
|
||||
|
@ -793,6 +834,13 @@ angular.module('podcasts.settings', ['podcasts.database'])
|
|||
;
|
||||
|
||||
angular.module('podcasts.importer', [])
|
||||
.service('opml', function() {
|
||||
return {
|
||||
import: function(url) {
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
.service('google', ['$q', '$http', 'feeds', function($q, $http, feeds) {
|
||||
return {
|
||||
import: function(email, password) {
|
||||
|
|
|
@ -14,4 +14,12 @@ angular.module('podcasts.utilities', [])
|
|||
}
|
||||
}
|
||||
})
|
||||
.service('url', ['$window', function($window) {
|
||||
return {
|
||||
url: $window.URL || $window.webkitURL,
|
||||
createObjectUrl: function(data) {
|
||||
return this.url.createObjectURL(data);
|
||||
}
|
||||
};
|
||||
}])
|
||||
;
|
||||
|
|
|
@ -320,7 +320,7 @@ var ixDbEz = (function () {
|
|||
// This is a workaround for the fact that chrome doesn't support blobs.
|
||||
// from: https://code.google.com/p/chromium/issues/detail?id=108012#c42
|
||||
//* when reading something that should be a blob, check if typeof value === "string"; if so, return new Blob([value], {type: 'application/octet-stream'}); otherwise return the value, which should be a Blob
|
||||
|
||||
console.log('Couldn\'t save the Blob, trying a workaround');
|
||||
angular.forEach(value, function(data, index) {
|
||||
if (Object.prototype.toString.call(data) === '[object Blob]') {
|
||||
var reader = new FileReader();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Podcast",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"description": "Podcast Manager for Firefox OS",
|
||||
"icons": {
|
||||
"128": "/blaIcon128.png"
|
||||
|
@ -14,8 +14,12 @@
|
|||
"permissions": {
|
||||
"storage": { "description": "Allow Podcast to store podcast information for offline use" },
|
||||
"systemXHR": { "description": "Allow Podcast to download podcast information" },
|
||||
"audio-channel-content": { "description": "Allow Podcast to play audio in the background" }
|
||||
"audio-channel-content": { "description": "Allow Podcast to play audio in the background" },
|
||||
"alarms": { "description": "Allow Podcast to update in the background" }
|
||||
},
|
||||
"installs_allowed_from": ["*"],
|
||||
"default_locale": "en"
|
||||
"default_locale": "en",
|
||||
"messages": [
|
||||
{ "alarm": "/index.html" }
|
||||
]
|
||||
}
|
17
partials/dev.html
Normal file
17
partials/dev.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<button ng-click="downloadFiles()">Download Files</button>
|
||||
<button ng-click="setAlarmTmp()">Set Alarm in 10 Seconds</button>
|
||||
<button ng-click="checkForPendingMessage()">Check for pending Messages</button>
|
||||
|
||||
|
||||
List of ALarms
|
||||
$scope.feeds
|
||||
<table>
|
||||
<thead>
|
||||
<td>Type</td>
|
||||
<td>Date</td>
|
||||
<td>more?</td>
|
||||
</thead>
|
||||
<tr ng-repeat="alarm in alarms">
|
||||
<td>alarm.</td>
|
||||
</tr>
|
||||
</table>
|
|
@ -1,5 +1,5 @@
|
|||
<form ng-submit="addFeed()">
|
||||
Add Feed: <input type="text" ng-model="newFeedUrl" /> <input type="submit" value="Add" />
|
||||
<input type="text" class="newFeedField" ng-model="newFeedUrl" placeholder="Add Feed" ng-blur="removePrefillIfNecessary()" ng-focus="preFillField()" /><input type="submit" value="Add" />
|
||||
</form>
|
||||
|
||||
<div ng-repeat="feed in feeds | orderBy:'title'" class="listItem"><!-- had ng-click="goToFeed(feed.id)" -->
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<label for="refreshInterval">Refresh Subscriptions:</label>
|
||||
<select name="refreshInterval" ng-change="changeInterval()" setting ng-model="settings.refreshInterval.value" ng-options="i.value as i.name for i in refreshIntervalOptions">
|
||||
</select>
|
||||
<select name="refreshInterval" id="refreshInterval" ng-change="changeInterval()" ng-model="settings.refreshInterval.value" ng-options="option.value as option.name for option in refreshIntervalOptions"></select>
|
||||
<br />
|
||||
<!--
|
||||
<label for="downloadOnWifi">Only Download on Wi-Fi</label>
|
||||
<input name="downloadOnWifi" type="checkbox" ng-change="changeDownloadOnWifi()" setting ng-model="settings.downloadOnWifi.value" />
|
||||
-->
|
||||
<!-- Store X Items Offline -->
|
||||
|
||||
<div ng-controller="TopLinksCtrl">
|
||||
<button ng-click="downloadFiles()">Download Files</button>
|
||||
<button ng-click="loadFixtures()">Load Fixtures</button><br />
|
||||
<a href="#/import/google">Import Feeds from Google</a>
|
||||
</div>
|
||||
<a href="#/dev">Developer Options</a>
|
||||
|
||||
<a href="#/info">About</a>
|
Loading…
Add table
Add a link
Reference in a new issue