diff --git a/dashboard-ui/css/librarybrowser.css b/dashboard-ui/css/librarybrowser.css index 3e57f2b24..baa988217 100644 --- a/dashboard-ui/css/librarybrowser.css +++ b/dashboard-ui/css/librarybrowser.css @@ -1519,14 +1519,14 @@ span.itemCommunityRating:not(:empty) + .userDataIcons { position: absolute; left: 0; top: 0; - padding: 23px 15px; + padding: 32px 15px; } .detailsMenuRightButton { position: absolute; right: 0; top: 0; - padding: 23px 15px; + padding: 32px 15px; } .detailsMenuHeaderWithLogo .detailsMenuLeftButton, .detailsMenuHeaderWithLogo .detailsMenuRightButton { diff --git a/dashboard-ui/scripts/backdrops.js b/dashboard-ui/scripts/backdrops.js index aa1702445..770e14e01 100644 --- a/dashboard-ui/scripts/backdrops.js +++ b/dashboard-ui/scripts/backdrops.js @@ -128,7 +128,7 @@ return false; } - if ($.browser.android && Dashboard.isRunningInCordova()) { + if ($.browser.android && AppInfo.isNativeApp) { return true; } diff --git a/dashboard-ui/scripts/connectlogin.js b/dashboard-ui/scripts/connectlogin.js index 37800f02c..c9066182c 100644 --- a/dashboard-ui/scripts/connectlogin.js +++ b/dashboard-ui/scripts/connectlogin.js @@ -83,7 +83,7 @@ if (mode == 'auto') { - if (Dashboard.isRunningInCordova()) { + if (AppInfo.isNativeApp) { loadAppConnection(page); return; } @@ -154,7 +154,7 @@ var link = 'http://emby.media'; $('.embyIntroDownloadMessage', page).html(Globalize.translate('EmbyIntroDownloadMessage', link)); - if (Dashboard.isRunningInCordova()) { + if (AppInfo.isNativeApp) { $('.skip', page).show(); } else { $('.skip', page).hide(); diff --git a/dashboard-ui/scripts/indexpage.js b/dashboard-ui/scripts/indexpage.js index 57d0e1b7d..65c33a7c3 100644 --- a/dashboard-ui/scripts/indexpage.js +++ b/dashboard-ui/scripts/indexpage.js @@ -654,7 +654,7 @@ function getDisplayPreferencesAppName() { - if (Dashboard.isRunningInCordova()) { + if (AppInfo.isNativeApp) { return 'Emby Mobile'; } diff --git a/dashboard-ui/scripts/livetvguide.js b/dashboard-ui/scripts/livetvguide.js index 817ca457d..888eb1a1e 100644 --- a/dashboard-ui/scripts/livetvguide.js +++ b/dashboard-ui/scripts/livetvguide.js @@ -403,6 +403,8 @@ function reloadPage(page) { + showLoadingMessage(page); + $('.guideRequiresUnlock', page).hide(); RegistrationServices.validateFeature('livetv').done(function () { diff --git a/dashboard-ui/scripts/mediaplayer-video.js b/dashboard-ui/scripts/mediaplayer-video.js index a9554b6c4..8c3efcb7b 100644 --- a/dashboard-ui/scripts/mediaplayer-video.js +++ b/dashboard-ui/scripts/mediaplayer-video.js @@ -1025,7 +1025,7 @@ self.canAutoPlayVideo = function () { - if (Dashboard.isRunningInCordova()) { + if (AppInfo.isNativeApp) { return true; } diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js index c733defe5..fdf31e7cf 100644 --- a/dashboard-ui/scripts/mediaplayer.js +++ b/dashboard-ui/scripts/mediaplayer.js @@ -1588,7 +1588,7 @@ self.canAutoPlayAudio = function () { - if (Dashboard.isRunningInCordova()) { + if (AppInfo.isNativeApp) { return true; } diff --git a/dashboard-ui/scripts/mypreferenceswebclient.js b/dashboard-ui/scripts/mypreferenceswebclient.js index a08028657..6059cafd4 100644 --- a/dashboard-ui/scripts/mypreferenceswebclient.js +++ b/dashboard-ui/scripts/mypreferenceswebclient.js @@ -87,7 +87,7 @@ $('.fldEnableBackdrops', page).show(); - if (Dashboard.isRunningInCordova()) { + if (AppInfo.isNativeApp) { $('.homePageConfigurationSection', page).hide(); } else { $('.homePageConfigurationSection', page).show(); diff --git a/dashboard-ui/scripts/selectserver.js b/dashboard-ui/scripts/selectserver.js index bbd9e4ad7..b521c45ef 100644 --- a/dashboard-ui/scripts/selectserver.js +++ b/dashboard-ui/scripts/selectserver.js @@ -373,7 +373,7 @@ servers = servers.slice(0); - if (Dashboard.isRunningInCordova()) { + if (AppInfo.isNativeApp) { servers.push({ Name: Globalize.translate('ButtonNewServer'), Id: 'new', diff --git a/dashboard-ui/scripts/site.js b/dashboard-ui/scripts/site.js index 354e39bb9..78bdfd466 100644 --- a/dashboard-ui/scripts/site.js +++ b/dashboard-ui/scripts/site.js @@ -49,7 +49,7 @@ var Dashboard = { isConnectMode: function () { - if (Dashboard.isRunningInCordova()) { + if (AppInfo.isNativeApp) { return true; } @@ -1366,14 +1366,20 @@ var Dashboard = { capabilities: function () { var caps = { - PlayableMediaTypes: "Audio,Video", + PlayableMediaTypes: ['Audio', 'Video'], - SupportedCommands: Dashboard.getSupportedRemoteCommands().join(','), - SupportsPersistentIdentifier: Dashboard.isRunningInCordova(), + SupportedCommands: Dashboard.getSupportedRemoteCommands(), + SupportsPersistentIdentifier: AppInfo.isNativeApp, SupportsMediaControl: true, SupportedLiveMediaTypes: ['Audio', 'Video'] }; + if (Dashboard.isRunningInCordova() && $.browser.android) { + caps.SupportsOfflineAccess = true; + caps.SupportsSync = true; + caps.SupportsContentUploading = true; + } + return caps; }, @@ -1389,7 +1395,7 @@ var Dashboard = { if (AppInfo.hasLowImageBandwidth) { // The native app can handle a little bit more than safari - if (Dashboard.isRunningInCordova()) { + if (AppInfo.isNativeApp) { quality -= 20; @@ -1588,6 +1594,7 @@ var AppInfo = {}; if (isCordova) { AppInfo.enableAppLayouts = true; AppInfo.hasKnownExternalPlayerSupport = true; + AppInfo.isNativeApp = true; } else { AppInfo.enableFooterNotifications = true; @@ -1615,11 +1622,11 @@ var AppInfo = {}; } //localStorage.clear(); - function createConnectionManager(appInfo, capabilities) { + function createConnectionManager(capabilities) { var credentialProvider = new MediaBrowser.CredentialProvider(); - window.ConnectionManager = new MediaBrowser.ConnectionManager(Logger, credentialProvider, appInfo.appName, appInfo.appVersion, appInfo.deviceName, appInfo.deviceId, capabilities); + window.ConnectionManager = new MediaBrowser.ConnectionManager(Logger, credentialProvider, AppInfo.appName, AppInfo.appVersion, AppInfo.deviceName, AppInfo.deviceId, capabilities); $(ConnectionManager).on('apiclientcreated', function (e, newApiClient) { @@ -1642,7 +1649,7 @@ var AppInfo = {}; } else { - apiClient = new MediaBrowser.ApiClient(Logger, Dashboard.serverAddress(), appInfo.appName, appInfo.appVersion, appInfo.deviceName, appInfo.deviceId); + apiClient = new MediaBrowser.ApiClient(Logger, Dashboard.serverAddress(), AppInfo.appName, AppInfo.appVersion, AppInfo.deviceName, AppInfo.deviceId); ConnectionManager.addApiClient(apiClient); } @@ -1652,7 +1659,7 @@ var AppInfo = {}; ApiClient.getDefaultImageQuality = Dashboard.getDefaultImageQuality; ApiClient.normalizeImageOptions = Dashboard.normalizeImageOptions; - if (!Dashboard.isRunningInCordova()) { + if (!AppInfo.isNativeApp) { Dashboard.importCss(ApiClient.getUrl('Branding/Css')); } } @@ -1722,7 +1729,7 @@ var AppInfo = {}; $(document.body).addClass('supporterMembershipDisabled'); } - if (Dashboard.isRunningInCordova()) { + if (AppInfo.isNativeApp) { $(document).addClass('nativeApp'); } @@ -1842,7 +1849,7 @@ var AppInfo = {}; requirejs(['thirdparty/cordova/connectsdk', 'scripts/registrationservices']); if ($.browser.android) { - requirejs(['thirdparty/cordova/android/immersive']); + requirejs(['thirdparty/cordova/android/androidcredentials', 'thirdparty/cordova/android/immersive']); } if ($.browser.safari) { @@ -1874,9 +1881,9 @@ var AppInfo = {}; setAppInfo(); - var appInfo = Dashboard.getAppInfo(appName, deviceId, deviceName); + $.extend(AppInfo, Dashboard.getAppInfo(appName, deviceId, deviceName)); - createConnectionManager(appInfo, capabilities); + createConnectionManager(capabilities); if (!resolveOnReady) { diff --git a/dashboard-ui/thirdparty/apiclient/connectionmanager.js b/dashboard-ui/thirdparty/apiclient/connectionmanager.js index edf8cf4f7..e56ddc188 100644 --- a/dashboard-ui/thirdparty/apiclient/connectionmanager.js +++ b/dashboard-ui/thirdparty/apiclient/connectionmanager.js @@ -88,10 +88,18 @@ return appVersion; }; + self.capabilities = function () { + return capabilities; + }; + self.deviceId = function () { return deviceId; }; + self.credentialProvider = function () { + return credentialProvider; + }; + self.connectUserId = function () { return credentialProvider.credentials().ConnectUserId; }; @@ -103,7 +111,7 @@ self.getLastUsedApiClient = function () { - var servers = credentialProvider.credentials().servers; + var servers = credentialProvider.credentials().Servers; servers.sort(function (a, b) { return b.DateLastAccessed - a.DateLastAccessed; @@ -122,7 +130,7 @@ apiClients.push(apiClient); - var existingServer = credentialProvider.credentials().servers.filter(function (s) { + var existingServer = credentialProvider.credentials().Servers.filter(function (s) { return stringEqualsIgnoreCase(s.ManualAddress, apiClient.serverAddress()); @@ -145,7 +153,7 @@ var credentials = credentialProvider.credentials(); - var server = credentials.servers.filter(function (s) { + var server = credentials.Servers.filter(function (s) { return s.Id == systemInfo.Id; @@ -159,7 +167,7 @@ apiClient.serverInfo(server); - credentialProvider.addOrUpdateServer(credentials.servers, server); + credentialProvider.addOrUpdateServer(credentials.Servers, server); credentialProvider.credentials(credentials); }); }; @@ -213,7 +221,7 @@ } var credentials = credentialProvider.credentials(); - var server = credentials.servers.filter(function (s) { + var server = credentials.Servers.filter(function (s) { return stringEqualsIgnoreCase(s.Id, serverId); })[0]; @@ -238,7 +246,7 @@ server.AccessToken = null; } - credentialProvider.addOrUpdateServer(credentials.servers, server); + credentialProvider.addOrUpdateServer(credentials.Servers, server); saveUserInfoIntoCredentials(server, result.User); credentialProvider.credentials(credentials); @@ -513,7 +521,7 @@ var credentials = credentialProvider.credentials(); - var servers = credentials.servers.filter(function (u) { + var servers = credentials.Servers.filter(function (u) { return u.UserLinkType != "Guest"; }); @@ -533,7 +541,7 @@ } } - credentials.servers = servers; + credentials.Servers = servers; credentials.ConnectAccessToken = null; credentials.ConnectUserId = null; @@ -622,7 +630,7 @@ findServersPromise.done(function (foundServers) { - var servers = credentials.servers.slice(0); + var servers = credentials.Servers.slice(0); mergeServers(servers, foundServers); mergeServers(servers, connectServers); @@ -632,7 +640,7 @@ return b.DateLastAccessed - a.DateLastAccessed; }); - credentials.servers = servers; + credentials.Servers = servers; credentialProvider.credentials(credentials); @@ -921,7 +929,7 @@ server.DateLastAccessed = new Date().getTime(); server.LastConnectionMode = connectionMode; - credentialProvider.addOrUpdateServer(credentials.servers, server); + credentialProvider.addOrUpdateServer(credentials.Servers, server); credentialProvider.credentials(credentials); var result = { @@ -1089,7 +1097,7 @@ var credentials = credentialProvider.credentials(); - var serverInfo = credentials.servers = credentials.servers.filter(function (s) { + var serverInfo = credentials.Servers = credentials.Servers.filter(function (s) { return s.ConnectServerId == serverId; }); @@ -1097,7 +1105,7 @@ credentials = credentialProvider.credentials(); - credentials.servers = credentials.servers.filter(function (s) { + credentials.Servers = credentials.Servers.filter(function (s) { return s.ConnectServerId != serverId; }); diff --git a/dashboard-ui/thirdparty/apiclient/credentials.js b/dashboard-ui/thirdparty/apiclient/credentials.js index 55a9653a2..57d167a40 100644 --- a/dashboard-ui/thirdparty/apiclient/credentials.js +++ b/dashboard-ui/thirdparty/apiclient/credentials.js @@ -8,12 +8,12 @@ var self = this; var credentials; - var key = 'servercredentials3'; + var key = 'servercredentials4'; function ensure() { credentials = credentials || JSON.parse(appStorage.getItem(key) || '{}'); - credentials.servers = credentials.servers || []; + credentials.Servers = credentials.Servers || []; } function get() { @@ -30,6 +30,8 @@ } else { self.clear(); } + + Events.trigger(self, 'credentialsupdated'); } self.clear = function () { diff --git a/dashboard-ui/thirdparty/cordova/android/androidcredentials.js b/dashboard-ui/thirdparty/cordova/android/androidcredentials.js new file mode 100644 index 000000000..7f939b85c --- /dev/null +++ b/dashboard-ui/thirdparty/cordova/android/androidcredentials.js @@ -0,0 +1,102 @@ +(function () { + + function updateCredentials() { + + console.log('sending updated credentials to ApiClientBridge'); + + var json = JSON.stringify(ConnectionManager.credentialProvider().credentials()); + var credentials = JSON.parse(json); + + for (var i = 0, length = credentials.Servers.length; i < length; i++) { + var server = credentials.Servers[i]; + + if (server.DateLastAccessed != null) { + server.DateLastAccessed = new Date(server.DateLastAccessed).toISOString(); + } + } + + json = JSON.stringify(credentials); + ApiClientBridge.updateCredentials(json); + } + + function initNativeConnectionManager() { + + console.log('initNativeConnectionManager'); + + var capabilities = ConnectionManager.capabilities(); + + ApiClientBridge.init(AppInfo.appName, AppInfo.appVersion, AppInfo.deviceId, AppInfo.deviceName, JSON.stringify(capabilities)); + + //initAjax(); + } + + var baseAjaxMethod; + var currentId = 0; + function getNewRequestId() { + var id = currentId++; + return id.toString(); + } + function initAjax() { + baseAjaxMethod = AjaxApi.ajax; + AjaxApi.ajax = sendRequest; + } + + function sendRequest(request) { + + if (request.data || request.contentType || request.dataType != 'json') { + return baseAjaxMethod(request); + } + + var deferred = DeferredBuilder.Deferred(); + + var id = getNewRequestId(); + + request.headers = request.headers || {}; + + if (request.dataType == 'json') { + request.headers.accept = 'application/json'; + } + + var requestHeaders = []; + for (name in request.headers) { + requestHeaders.push(name + "=" + request.headers[name]); + } + + ApiClientBridge.sendRequest(request.url, request.type, requestHeaders.join('|||||'), "window.AndroidAjax.onResponse", id); + + Events.on(AndroidAjax, 'response' + id, function (e, status, response) { + + Events.off(AndroidAjax, 'response' + id); + + response = decodeURIComponent(response); + + if (status >= 400) { + alert(status); + deferred.reject(); + } + else if (request.dataType == 'json') { + deferred.resolveWith(null, [JSON.parse(response)]); + } + else { + deferred.resolveWith(null, [response]); + } + + }); + + return deferred.promise(); + } + + Events.on(ConnectionManager.credentialProvider(), 'credentialsupdated', updateCredentials); + + updateCredentials(); + initNativeConnectionManager(); + + window.AndroidAjax = { + + onResponse: function (id, status, response) { + + Events.trigger(AndroidAjax, 'response' + id, [status, response]); + } + }; + +})(); \ No newline at end of file diff --git a/dashboard-ui/thirdparty/cordova/chromecast.js b/dashboard-ui/thirdparty/cordova/chromecast.js index e5962880f..13b4aaf4e 100644 --- a/dashboard-ui/thirdparty/cordova/chromecast.js +++ b/dashboard-ui/thirdparty/cordova/chromecast.js @@ -468,11 +468,11 @@ currentWebAppSession = session.acquire(); currentWebAppSession.on('message', handleMessage); - currentWebAppSession.on('disconnect', handleSessionDisconnect); currentWebAppSession.connect().success(function () { console.log('session.connect succeeded'); + currentWebAppSession.on('disconnect', handleSessionDisconnect); MediaController.setActivePlayer(PlayerName, convertDeviceToTarget(device)); currentDeviceFriendlyName = device.getFriendlyName(); @@ -507,12 +507,57 @@ session.release(); } + if (currentDevice != null) { + currentDevice.off("ready"); + currentDevice.off("disconnect"); + + currentDevice.disconnect(); + } + currentWebAppSession = null; currentPairedDeviceId = null; currentDeviceFriendlyName = null; currentDevice = null; } + function tryLaunchWebSession(device) { + + console.log('calling launchWebApp'); + device.getWebAppLauncher().launchWebApp(ApplicationID).success(function (session) { + + console.log('launchWebApp success. calling onSessionConnected'); + setupWebAppSession(device, session); + + }).error(function (err1) { + + console.log('launchWebApp error:' + JSON.stringify(err1)); + + }); + } + + function tryJoinWebSession(device, enableRetry) { + + console.log('calling joinWebApp'); + device.getWebAppLauncher().joinWebApp(ApplicationID).success(function (session) { + + console.log('joinWebApp success. calling onSessionConnected'); + setupWebAppSession(device, session); + + }).error(function (err) { + + console.log('joinWebApp error: ' + JSON.stringify(err)); + + if (enableRetry) { + tryJoinWebSession(device, false); + return; + } + + console.log('calling launchWebApp'); + tryLaunchWebSession(device); + + }); + } + function launchWebApp(device) { // First try to join existing session. If it fails, launch a new one @@ -524,7 +569,7 @@ }).error(function (err) { - console.log('joinWebApp error: ' + JSON.stringify(err) + '. calling joinWebApp'); + console.log('joinWebApp error: ' + JSON.stringify(err) + '. calling launchWebApp'); device.getWebAppLauncher().launchWebApp(ApplicationID).success(function (session) { @@ -547,13 +592,9 @@ return; } - console.log('calling launchWebApp'); + console.log('creating webAppSession'); - setTimeout(function () { - - launchWebApp(device); - - }, 0); + launchWebApp(device); } self.tryPair = function (target) { @@ -610,7 +651,7 @@ if (newTarget.id != currentPairedDeviceId) { if (currentWebAppSession) { console.log('Disconnecting from chromecast'); - currentWebAppSession.disconnect(); + cleanupSession(); } } }