From b4e5a33e98c5792e86148ac22bde08689fc8ef89 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Fri, 28 Sep 2012 12:21:22 +0200 Subject: [PATCH 01/31] Implemented database access of media management in Admin Console. Add/create, query and delete already work, update missing, yet. --- .../META-INF/MANIFEST.MF | 29 +- .../persistence/media/DBMediaTrack.java | 21 + .../domain/persistence/media/MediaDB.java | 24 ++ .../persistence/media/MediaDBFactory.java | 13 + .../persistence/media/MediaObjectFactory.java | 11 + .../media/impl/CollectionNames.java | 50 +++ .../persistence/media/impl/DbNames.java | 13 + .../media/impl/MediaDBFactoryImpl.java | 38 ++ .../persistence/media/impl/MediaDBImpl.java | 93 ++++ .../media/impl/MediaObjectFactoryImpl.java | 19 + .../persistence/media/impl/MongoUtils.java | 39 ++ .../META-INF/MANIFEST.MF | 87 ++-- java/com.sap.sailing.gwt.ui/WEB-INF/web.xml | 226 +++++----- .../adminconsole/AdminConsoleEntryPoint.java | 1 + .../gwt/ui/adminconsole/MediaPanel.java | 320 ++++++++++++++ .../gwt/ui/adminconsole/MediaTrackDialog.java | 122 ++++++ .../gwt/ui/client/AbstractEntryPoint.java | 20 +- .../sailing/gwt/ui/client/MediaPlayer.java | 19 + .../sailing/gwt/ui/client/MediaService.java | 22 + .../gwt/ui/client/MediaServiceAsync.java | 21 + .../sailing/gwt/ui/client/StringMessages.java | 5 + .../gwt/ui/client/StringMessages.properties | 7 +- .../ui/client/StringMessages_de.properties | 7 +- .../ui/leaderboard/LeaderboardEntryPoint.java | 41 +- .../gwt/ui/leaderboard/TVViewPanel.java | 74 ++-- .../gwt/ui/raceboard/MediaSelector.java | 133 ++++++ .../gwt/ui/raceboard/RaceBoardEntryPoint.java | 407 +++++++++--------- .../gwt/ui/raceboard/RaceBoardPanel.java | 138 +++--- .../sailing/gwt/ui/raceboard/VideoPopup.java | 92 ++++ .../sailing/gwt/ui/raceboard/VideoTrack.java | 37 ++ .../gwt/ui/server/MediaServiceImpl.java | 79 ++++ .../gwt/ui/shared/media/MediaTrack.java | 66 +++ .../.classpath | 7 + .../.gitignore | 1 + .../.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 8 + .../.settings/org.eclipse.m2e.core.prefs | 5 + .../.settings/org.eclipse.pde.core.prefs | 4 + .../META-INF/MANIFEST.MF | 16 + .../build.properties | 4 + .../pom.xml | 12 + .../persistence/test/AbstractMongoDBTest.java | 51 +++ .../media/persistence/test/InitialLoad.java | 13 + .../media/persistence/test/TestCRUDMedia.java | 90 ++++ .../configuration/jetty/etc/realm.properties | 48 +-- 45 files changed, 2052 insertions(+), 509 deletions(-) create mode 100644 java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java create mode 100644 java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java create mode 100644 java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDBFactory.java create mode 100644 java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaObjectFactory.java create mode 100644 java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/CollectionNames.java create mode 100644 java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/DbNames.java create mode 100644 java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBFactoryImpl.java create mode 100644 java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java create mode 100644 java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaObjectFactoryImpl.java create mode 100644 java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MongoUtils.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaPlayer.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoTrack.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java create mode 100644 java/com.sap.sailing.media.persistence.test/.classpath create mode 100644 java/com.sap.sailing.media.persistence.test/.gitignore create mode 100644 java/com.sap.sailing.media.persistence.test/.project create mode 100644 java/com.sap.sailing.media.persistence.test/.settings/org.eclipse.jdt.core.prefs create mode 100644 java/com.sap.sailing.media.persistence.test/.settings/org.eclipse.m2e.core.prefs create mode 100644 java/com.sap.sailing.media.persistence.test/.settings/org.eclipse.pde.core.prefs create mode 100644 java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF create mode 100644 java/com.sap.sailing.media.persistence.test/build.properties create mode 100644 java/com.sap.sailing.media.persistence.test/pom.xml create mode 100644 java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/AbstractMongoDBTest.java create mode 100644 java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/InitialLoad.java create mode 100644 java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestCRUDMedia.java diff --git a/java/com.sap.sailing.domain.persistence/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.persistence/META-INF/MANIFEST.MF index d868d5ffa5c..b6dc9bac419 100755 --- a/java/com.sap.sailing.domain.persistence/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.persistence/META-INF/MANIFEST.MF @@ -1,14 +1,15 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Persistence -Bundle-SymbolicName: com.sap.sailing.domain.persistence -Bundle-Version: 1.0.0.qualifier -Bundle-Vendor: SAP -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Require-Bundle: com.sap.sailing.mongodb, - com.sap.sailing.domain, - com.mongodb.driver;bundle-version="2.6.2", - com.sap.sailing.domain.common -Export-Package: com.sap.sailing.domain.persistence, - com.sap.sailing.domain.persistence.impl;x-friends:="com.sap.sailing.mongodb.test,com.sap.sailing.domain.swisstimingadapter.test" -Bundle-ActivationPolicy: lazy +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Persistence +Bundle-SymbolicName: com.sap.sailing.domain.persistence +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: SAP +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Require-Bundle: com.sap.sailing.mongodb, + com.sap.sailing.domain, + com.mongodb.driver;bundle-version="2.6.2", + com.sap.sailing.domain.common +Export-Package: com.sap.sailing.domain.persistence, + com.sap.sailing.domain.persistence.impl;x-friends:="com.sap.sailing.mongodb.test,com.sap.sailing.domain.swisstimingadapter.test", + com.sap.sailing.domain.persistence.media +Bundle-ActivationPolicy: lazy diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java new file mode 100644 index 00000000000..50ee4d74816 --- /dev/null +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java @@ -0,0 +1,21 @@ +package com.sap.sailing.domain.persistence.media; + +import java.util.Date; + +public class DBMediaTrack { + + public final String title; + public final String url; + public final Date startTime; + public final String mimeType; + public final String mimeSubType; + + public DBMediaTrack(String title, String url, Date startTime, String mimeType, String mimeSubType) { + super(); + this.title = title; + this.url = url; + this.startTime = startTime; + this.mimeType = mimeType; + this.mimeSubType = mimeSubType; + } +} diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java new file mode 100644 index 00000000000..37fbd9d6eed --- /dev/null +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java @@ -0,0 +1,24 @@ +package com.sap.sailing.domain.persistence.media; + +import java.util.Date; +import java.util.List; + +import com.mongodb.DBObject; + +/** + * Offers CRUD methods for mongo representation of media track objects. + * + * @author Jens Rommel (d047974) + * + */ +public interface MediaDB { + + void insertMediaTrack(String videoTitle, String url, Date startTime, String mediaType, String mediaSubType); + + DBMediaTrack loadMediaTrack(String videoTitle); + + List loadAllMediaTracks(); + + void deleteMediaTrack(String title); + +} diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDBFactory.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDBFactory.java new file mode 100644 index 00000000000..448690c091b --- /dev/null +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDBFactory.java @@ -0,0 +1,13 @@ +package com.sap.sailing.domain.persistence.media; + +import com.sap.sailing.domain.persistence.media.impl.MediaDBFactoryImpl; +import com.sap.sailing.mongodb.MongoDBService; + +public interface MediaDBFactory { + MediaDBFactory INSTANCE = new MediaDBFactoryImpl(); + + MediaObjectFactory getDefaultDomainObjectFactory(); + MediaObjectFactory getDomainObjectFactory(MongoDBService mongoDBService); + MediaDB getDefaultMediaDB(); + MediaDB getMediaDB(MongoDBService mongoDBService); +} diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaObjectFactory.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaObjectFactory.java new file mode 100644 index 00000000000..678ae3a8983 --- /dev/null +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaObjectFactory.java @@ -0,0 +1,11 @@ +package com.sap.sailing.domain.persistence.media; + + +/** + * Offers methods to load media objects from a Mongo DB + * + * @author Jens Rommel (d047974) + * + */ +public interface MediaObjectFactory { +} diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/CollectionNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/CollectionNames.java new file mode 100644 index 00000000000..d64a6e15150 --- /dev/null +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/CollectionNames.java @@ -0,0 +1,50 @@ +package com.sap.sailing.domain.persistence.media.impl; + +/** + * Defines literals providing the names for MongoDB collections. The literal documentation described the semantics + * of the collection identified by that literal. + * + * @author Axel Uhl (D043530) + * + */ +public enum CollectionNames { + /** + * Stores the wind fixes recorded from persistent wind sources. + */ + WIND_TRACKS, + + /** + * Stores the leaderboards with their names, score corrections, competitor display name overrides, race + * columns and their fleets as well as the per-fleet tracked race assignments. + */ + LEADERBOARDS, + + /** + * Stores the leaderboard group configurations with references to the {@link #LEADERBOARDS} collection + */ + LEADERBOARD_GROUPS, + + /** + * Top-level event information about events such as Kiel Week 2011, or IDM Travem�nde 2011, including name and + * course areas. + */ + EVENTS, + + /** + * Stores regatta definitions including their series layout and fleets and race columns. Regattas can reference + * the event from the {@link #EVENTS} collection to which they belong. + */ + REGATTAS, + + /** + * Stores boat class-specific master data such as the class's hull length, logo, name, number of sailors, etc. + * To be implemented in future versions. + */ + BOAT_CLASSES, + + /** + * Stores the mapping of {@link RaceDefinition#getId race IDs} to regatta names for automatic re-association + * when tracking races again without explicitly specifying a regatta. + */ + REGATTA_FOR_RACE_ID; +} diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/DbNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/DbNames.java new file mode 100644 index 00000000000..42616bbb1dc --- /dev/null +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/DbNames.java @@ -0,0 +1,13 @@ +package com.sap.sailing.domain.persistence.media.impl; + +public class DbNames { + + public enum Fields { + MEDIA_TITLE, MEDIA_URL, STARTTIME, MIME_TYPE, MIME_SUBTYPE; + } + + public enum Collections { + VIDEOS; + } + +} diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBFactoryImpl.java new file mode 100644 index 00000000000..de6f4e2d946 --- /dev/null +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBFactoryImpl.java @@ -0,0 +1,38 @@ +package com.sap.sailing.domain.persistence.media.impl; + +import com.sap.sailing.domain.persistence.media.MediaDB; +import com.sap.sailing.domain.persistence.media.MediaDBFactory; +import com.sap.sailing.domain.persistence.media.MediaObjectFactory; +import com.sap.sailing.mongodb.MongoDBService; + +public class MediaDBFactoryImpl implements MediaDBFactory { + private final MediaObjectFactory defaultMediaObjectFactory; + private final MediaDB defaultMediaDB; + + public MediaDBFactoryImpl() { + super(); + this.defaultMediaObjectFactory = new MediaObjectFactoryImpl(MongoDBService.INSTANCE.getDB()); + this.defaultMediaDB = new MediaDBImpl(MongoDBService.INSTANCE.getDB()); + } + + @Override + public MediaObjectFactory getDefaultDomainObjectFactory() { + return defaultMediaObjectFactory; + } + + @Override + public MediaObjectFactory getDomainObjectFactory(MongoDBService mongoDBService) { + return new MediaObjectFactoryImpl(mongoDBService.getDB()); + } + + @Override + public MediaDB getDefaultMediaDB() { + return defaultMediaDB; + } + + @Override + public MediaDB getMediaDB(MongoDBService mongoDBService) { + return new MediaDBImpl(mongoDBService.getDB()); + } + +} diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java new file mode 100644 index 00000000000..df23c52d238 --- /dev/null +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java @@ -0,0 +1,93 @@ +package com.sap.sailing.domain.persistence.media.impl; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.logging.Logger; + +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.DBCursor; +import com.mongodb.DBObject; +import com.mongodb.WriteResult; +import com.sap.sailing.domain.persistence.impl.FieldNames; +import com.sap.sailing.domain.persistence.media.DBMediaTrack; +import com.sap.sailing.domain.persistence.media.MediaDB; + +public class MediaDBImpl implements MediaDB { + + private static Logger logger = Logger.getLogger(MediaDBImpl.class.getName()); + private final DB database; + + public MediaDBImpl(DB database) { + super(); + this.database = database; + } + + @Override + public void insertMediaTrack(String videoTitle, String url, Date startTime, String mediaType, String mediaSubType) { + BasicDBObject dbVideo = new BasicDBObject(); + dbVideo.put(DbNames.Fields.MEDIA_TITLE.name(), videoTitle); + dbVideo.put(DbNames.Fields.MEDIA_URL.name(), url); + dbVideo.put(DbNames.Fields.STARTTIME.name(), startTime); + dbVideo.put(DbNames.Fields.MIME_TYPE.name(), mediaType); + dbVideo.put(DbNames.Fields.MIME_SUBTYPE.name(), mediaSubType); + DBCollection dbVideos = getVideoCollection(); + dbVideos.insert(dbVideo); + } + + private DBCollection getVideoCollection() { + try { + DBCollection dbVideos = database.getCollection(DbNames.Collections.VIDEOS.name()); + dbVideos.ensureIndex(DbNames.Collections.VIDEOS.name()); + return dbVideos; + } catch (NullPointerException e) { + // sometimes, for reasons yet to be clarified, ensuring an index on the name field causes an NPE + throw new RuntimeException(e); + } + } + + @Override + public DBMediaTrack loadMediaTrack(String videoTitle) { + DBCursor cursor = getVideoCollection().find(new BasicDBObject(DbNames.Fields.MEDIA_TITLE.name(), videoTitle)); + switch (cursor.count()) { + case 0: + throw new RuntimeException("Video not found with title: " + videoTitle); + case 1: + DBObject dbObject = cursor.next(); + DBMediaTrack dbMediaTrack = createMediaObjectFromDB(dbObject); + return dbMediaTrack; + default: + throw new RuntimeException("Ambiguous videos not found with title: " + videoTitle); + + } + } + + private DBMediaTrack createMediaObjectFromDB(DBObject dbObject) { + String title = (String) dbObject.get(DbNames.Fields.MEDIA_TITLE.name()); + String url = (String) dbObject.get(DbNames.Fields.MEDIA_URL.name()); + Date startTime = (Date) dbObject.get(DbNames.Fields.STARTTIME.name()); + String mimeType = (String) dbObject.get(DbNames.Fields.MIME_TYPE.name()); + String mimeSubType = (String) dbObject.get(DbNames.Fields.MIME_SUBTYPE.name()); + DBMediaTrack dbMediaTrack = new DBMediaTrack(title, url, startTime, mimeType, mimeSubType); + return dbMediaTrack; + } + + @Override + public List loadAllMediaTracks() { + DBCursor cursor = getVideoCollection().find(); + List result = new ArrayList<>(cursor.count()); + while (cursor.hasNext()) { + result.add(createMediaObjectFromDB(cursor.next())); + } + return result; + } + + @Override + public void deleteMediaTrack(String title) { + BasicDBObject dbObject = new BasicDBObject(); + dbObject.put(DbNames.Fields.MEDIA_TITLE.name(), title); + getVideoCollection().remove(dbObject); + } +} diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaObjectFactoryImpl.java new file mode 100644 index 00000000000..2f6a07b9339 --- /dev/null +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaObjectFactoryImpl.java @@ -0,0 +1,19 @@ +package com.sap.sailing.domain.persistence.media.impl; + +import java.util.logging.Logger; + +import com.mongodb.DB; +import com.sap.sailing.domain.persistence.media.MediaObjectFactory; + +public class MediaObjectFactoryImpl implements MediaObjectFactory { + + private static final Logger logger = Logger.getLogger(MediaObjectFactoryImpl.class.getName()); + + private final DB database; + + public MediaObjectFactoryImpl(DB db) { + super(); + this.database = db; + } + +} diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MongoUtils.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MongoUtils.java new file mode 100644 index 00000000000..2ac17d56e0a --- /dev/null +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MongoUtils.java @@ -0,0 +1,39 @@ +package com.sap.sailing.domain.persistence.media.impl; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + +public class MongoUtils { + /** + * Key names in MongoDB must not start with a $ sign and must not contain a "." character. This method escapes + * those. {@link #unescapeDollarAndDot} is the inverse function. + * + * @param key + * a non-null string which may have length 0 + * @return a string that can be used as a key in a MongoDB key-value pair and that can be decoded again using + * {@link #unescapeDollarAndDot(String)}. + */ + public static String escapeDollarAndDot(String key) { + String result = key.replace("%", "%25").replace("+", "%2B"); + if (result.length() > 0 && result.charAt(0) == '$') { + result = "%24"+result.substring(1); + } + return result.replace(".", "%2E"); + } + + /** + * @see #escapeDollarAndDot(String) + * @param key + * a non-null string which may have length 0 and that was returned by + * {@link #escapeDollarAndDot(String)}. + * @return the string originally passed to {@link #escapeDollarAndDot(String)} + */ + public static String unescapeDollarAndDot(String escapedKey) { + try { + return URLDecoder.decode(escapedKey, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("How come this VM doesn't know UTF-8?"); + } + } + +} diff --git a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF index 6271e0c699b..957f9c8d90c 100755 --- a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF @@ -1,43 +1,44 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Sailing App GWT UI -Bundle-SymbolicName: com.sap.sailing.gwt.ui -Bundle-Version: 1.0.0.qualifier -Bundle-Vendor: SAP -Bundle-RequiredExecutionEnvironment: JavaSE-1.6 -Require-Bundle: com.sap.sailing.domain, - com.sap.sailing.server, - com.sap.sailing.domain.tractracadapter, - com.sap.sailing.mongodb, - com.mongodb.driver;bundle-version="2.6.2", - com.sap.sailing.domain.swisstimingadapter, - com.sap.sailing.domain.persistence, - com.sap.sailing.domain.swisstimingadapter.persistence, - com.sap.sailing.domain.tractracadapter.persistence, - com.google.gwt.osgi;bundle-version="2.4.0", - com.sap.sailing.domain.common, - org.moxieapps.gwt.highcharts;bundle-version="1.1.4", - com.sap.sailing.geocoding, - com.sap.sailing.server.replication, - com.googlecode.java-diff-utils;bundle-version="1.3.0", - com.sap.sailing.freg.resultimport -Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator -Bundle-ActivationPolicy: lazy -Export-Package: com.sap.sailing.gwt.ui.client;x-friends:="com.sap.sailing.gwt.ui.test", - com.sap.sailing.gwt.ui.adminconsole;x-friends:="com.sap.sailing.gwt.ui.test", - com.sap.sailing.gwt.ui.leaderboard;x-friends:="com.sap.sailing.gwt.ui.test", - com.sap.sailing.gwt.ui.leaderboardedit;x-friends:="com.sap.sailing.gwt.ui.test", - com.sap.sailing.gwt.ui.server;x-friends:="com.sap.sailing.gwt.ui.test", - com.sap.sailing.gwt.ui.shared;x-friends:="com.sap.sailing.gwt.ui.test" -Import-Package: javax.jms;version="1.1.0", - javax.servlet;version="2.5.0", - javax.servlet.http;version="2.5.0", - org.json.simple, - org.json.simple.parser, - org.osgi.framework, - org.osgi.util.tracker -Web-ContextPath: /gwt -Bundle-ClassPath: WEB-INF/classes/, - WEB-INF/lib/gwt-maps.jar, - WEB-INF/lib/gwt-servlet.jar -TrackLifecycle: true +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Sailing App GWT UI +Bundle-SymbolicName: com.sap.sailing.gwt.ui +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: SAP +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Require-Bundle: com.sap.sailing.domain, + com.sap.sailing.server, + com.sap.sailing.domain.tractracadapter, + com.sap.sailing.mongodb, + com.mongodb.driver;bundle-version="2.6.2", + com.sap.sailing.domain.swisstimingadapter, + com.sap.sailing.domain.persistence, + com.sap.sailing.domain.swisstimingadapter.persistence, + com.sap.sailing.domain.tractracadapter.persistence, + com.google.gwt.osgi;bundle-version="2.4.0", + com.sap.sailing.domain.common, + org.moxieapps.gwt.highcharts;bundle-version="1.1.4", + com.sap.sailing.geocoding, + com.sap.sailing.server.replication, + com.googlecode.java-diff-utils;bundle-version="1.3.0", + com.sap.sailing.freg.resultimport +Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator +Bundle-ActivationPolicy: lazy +Export-Package: com.sap.sailing.gwt.ui.adminconsole;x-friends:="com.sap.sailing.gwt.ui.test", + com.sap.sailing.gwt.ui.client;x-friends:="com.sap.sailing.gwt.ui.test", + com.sap.sailing.gwt.ui.leaderboard;x-friends:="com.sap.sailing.gwt.ui.test", + com.sap.sailing.gwt.ui.leaderboardedit;x-friends:="com.sap.sailing.gwt.ui.test", + com.sap.sailing.gwt.ui.server;x-friends:="com.sap.sailing.gwt.ui.test", + com.sap.sailing.gwt.ui.shared;x-friends:="com.sap.sailing.gwt.ui.test", + com.sap.sailing.gwt.ui.shared.media +Import-Package: javax.jms;version="1.1.0", + javax.servlet;version="2.5.0", + javax.servlet.http;version="2.5.0", + org.json.simple, + org.json.simple.parser, + org.osgi.framework, + org.osgi.util.tracker +Web-ContextPath: /gwt +Bundle-ClassPath: WEB-INF/classes/, + WEB-INF/lib/gwt-maps.jar, + WEB-INF/lib/gwt-servlet.jar +TrackLifecycle: true diff --git a/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml b/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml index b9f4927bbdb..bd340c5f63d 100755 --- a/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml +++ b/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml @@ -1,108 +1,118 @@ - - - - - GWTCacheControlServletFilter - com.sap.sailing.server.GWTCacheControlServletFilter - - - GWTCacheControlServletFilter - /* - - - - Sailing Service - SailingService - com.sap.sailing.gwt.ui.server.SailingServiceImpl - - - SailingService - /sailing - - - - User Management Service - UserManagementService - com.sap.sailing.gwt.ui.server.UserManagementServiceImpl - - - UserManagementService - /usermanagement - - - - - remoteLoggingServlet - com.google.gwt.logging.server.RemoteLoggingServiceImpl - - - - remoteLoggingServlet - /com.sap.sailing.gwt.ui.AdminConsole/remote_logging - - - - - Administration pages - /AdminConsole.html - - - administrator - - - - - - - - Leaderboard editing pages - /LeaderboardEditing.html - - - administrator - moderator - - - - - - user management pages - /UserManagement.html - - - administrator - - - - - BASIC - SailingServerRealm - - + + + + + GWTCacheControlServletFilter + com.sap.sailing.server.GWTCacheControlServletFilter + + + GWTCacheControlServletFilter + /* + + + + Sailing Service + SailingService + com.sap.sailing.gwt.ui.server.SailingServiceImpl + + + SailingService + /sailing + + + + Media Service + MediaService + com.sap.sailing.gwt.ui.server.MediaServiceImpl + + + MediaService + /media + + + + User Management Service + UserManagementService + com.sap.sailing.gwt.ui.server.UserManagementServiceImpl + + + UserManagementService + /usermanagement + + + + + remoteLoggingServlet + com.google.gwt.logging.server.RemoteLoggingServiceImpl + + + + remoteLoggingServlet + /com.sap.sailing.gwt.ui.AdminConsole/remote_logging + + + + + Administration pages + /AdminConsole.html + + + administrator + + + + + + + + Leaderboard editing pages + /LeaderboardEditing.html + + + administrator + moderator + + + + + + user management pages + /UserManagement.html + + + administrator + + + + + BASIC + SailingServerRealm + + diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java index 86b825d3bf9..7e11e1a7585 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java @@ -72,6 +72,7 @@ public class AdminConsoleEntryPoint extends AbstractEntryPoint implements Regatt regattaDisplayers.add(leaderboardConfigPanel); tabPanel.add(new FregResultImportUrlPanel(sailingService, this, stringMessages), stringMessages.fregResultImportUrls(), /* asHTML */ false); tabPanel.add(new ReplicationPanel(sailingService, this, stringMessages), stringMessages.replication(), /* asHTML */ false); + tabPanel.add(new MediaPanel(mediaService, this, stringMessages), stringMessages.mediaPanel(), /* asHTML */ false); tabPanel.selectTab(0); tabPanel.addSelectionHandler(new SelectionHandler() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java new file mode 100644 index 00000000000..f3a5e0b1a4a --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java @@ -0,0 +1,320 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.Comparator; +import java.util.List; + +import com.google.gwt.cell.client.ActionCell; +import com.google.gwt.cell.client.EditTextCell; +import com.google.gwt.cell.client.FieldUpdater; +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; +import com.google.gwt.user.cellview.client.CellTable; +import com.google.gwt.user.cellview.client.Column; +import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler; +import com.google.gwt.user.cellview.client.IdentityColumn; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.view.client.DefaultSelectionEventManager; +import com.google.gwt.view.client.ListDataProvider; +import com.google.gwt.view.client.SelectionModel; +import com.google.gwt.view.client.SingleSelectionModel; +import com.sap.sailing.gwt.ui.client.ErrorReporter; +import com.sap.sailing.gwt.ui.client.MediaServiceAsync; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack; + +/** + * Table inspired by http://gwt.google.com/samples/Showcase/Showcase.html#!CwCellTable + * + * @author D047974 + * + */ +public class MediaPanel extends FlowPanel { + + private static final DateTimeFormat TIME_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.HOUR24_MINUTE_SECOND); + private static final DateTimeFormat DATETIME_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.DATE_TIME_MEDIUM); + // private Calendar calender = new GregorianCalendar(); + + private final MediaServiceAsync mediaService; + private final ErrorReporter errorReporter; + private final StringMessages stringMessages; + private final Grid mediaTracks; + private CellTable mediaTracksTable; + private ListDataProvider mediaTrackListDataProvider = new ListDataProvider(); + + public MediaPanel(MediaServiceAsync mediaService, ErrorReporter errorReporter, StringMessages stringMessages) { + this.mediaService = mediaService; + this.stringMessages = stringMessages; + this.errorReporter = errorReporter; + mediaTracks = new Grid(); + mediaTracks.resizeColumns(3); + add(mediaTracks); + Button refreshButton = new Button(stringMessages.refresh()); + refreshButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + loadMediaTracks(); + } + + }); + add(refreshButton); + Button addButton = new Button(stringMessages.add()); + addButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + addMediaTrack(); + } + }); + add(addButton); + + createMediaTracksTable(); + + } + + protected void loadMediaTracks() { + mediaTrackListDataProvider.getList().clear(); + mediaService.getAllMediaTracks(new AsyncCallback>() { + + @Override + public void onFailure(Throwable t) { + errorReporter.reportError(t.toString()); + } + + @Override + public void onSuccess(List allMediaTracks) { + mediaTrackListDataProvider.getList().addAll(allMediaTracks); + mediaTrackListDataProvider.refresh(); + } + }); + + } + + private void createMediaTracksTable() { + AdminConsoleTableResources tableResources = GWT.create(AdminConsoleTableResources.class); + // Create a CellTable. + + // Set a key provider that provides a unique key for each contact. If key is + // used to identify contacts when fields (such as the name and address) + // change. + mediaTracksTable = new CellTable(1000, tableResources); + mediaTracksTable.setWidth("100%"); + + // Attach a column sort handler to the ListDataProvider to sort the list. + ListHandler sortHandler = new ListHandler(mediaTrackListDataProvider.getList()); + mediaTracksTable.addColumnSortHandler(sortHandler); + + // Add a selection model so we can select cells. + final SelectionModel selectionModel = new SingleSelectionModel(); + mediaTracksTable.setSelectionModel(selectionModel, + DefaultSelectionEventManager. createDefaultManager()); + + // Initialize the columns. + initTableColumns(selectionModel, sortHandler); + + mediaTrackListDataProvider.addDataDisplay(mediaTracksTable); + add(mediaTracksTable); + } + + /** + * Add the columns to the table. + */ + private void initTableColumns(final SelectionModel selectionModel, + ListHandler sortHandler) { + // // Checkbox column. This table will uses a checkbox column for selection. + // // Alternatively, you can call cellTable.setSelectionEnabled(true) to enable + // // mouse selection. + // Column checkColumn = new Column(new CheckboxCell(true, false)) + // { + // @Override + // public Boolean getValue(ContactInfo object) { + // // Get the value from the selection model. + // return selectionModel.isSelected(object); + // } + // }; + // cellTable.addColumn(checkColumn, SafeHtmlUtils.fromSafeConstant("
")); + // cellTable.setColumnWidth(checkColumn, 40, Unit.PX); + + // media title + Column titleColumn = new Column(new EditTextCell()) { + @Override + public String getValue(MediaTrack mediaTrack) { + return mediaTrack.title; + } + }; + titleColumn.setSortable(true); + sortHandler.setComparator(titleColumn, new Comparator() { + public int compare(MediaTrack mediaTrack1, MediaTrack mediaTrack2) { + return mediaTrack1.title.compareTo(mediaTrack2.title); + } + }); + mediaTracksTable.addColumn(titleColumn, stringMessages.title()); + titleColumn.setFieldUpdater(new FieldUpdater() { + public void update(int index, MediaTrack mediaTrack, String newTitle) { + // Called when the user changes the value. + mediaTrack.title = newTitle; + mediaTrackListDataProvider.refresh(); + } + }); + mediaTracksTable.setColumnWidth(titleColumn, 20, Unit.PCT); + + // url + Column urlColumn = new Column(new EditTextCell()) { + @Override + public String getValue(MediaTrack mediaTrack) { + return mediaTrack.url; + } + }; + urlColumn.setSortable(true); + sortHandler.setComparator(urlColumn, new Comparator() { + public int compare(MediaTrack mediaTrack1, MediaTrack mediaTrack2) { + return mediaTrack1.url.compareTo(mediaTrack2.url); + } + }); + mediaTracksTable.addColumn(urlColumn, stringMessages.url()); + urlColumn.setFieldUpdater(new FieldUpdater() { + public void update(int index, MediaTrack mediaTrack, String newUrl) { + // Called when the user changes the value. + mediaTrack.url = newUrl; + mediaTrackListDataProvider.refresh(); + } + }); + mediaTracksTable.setColumnWidth(urlColumn, 100, Unit.PCT); + + // media types + // final Category[] categories = ContactDatabase.get().queryCategories(); + // List categoryNames = new ArrayList(); + // for (Category category : categories) { + // categoryNames.add(category.getDisplayName()); + // } + // SelectionCell categoryCell = new SelectionCell(categoryNames); + // Column categoryColumn = new Column(categoryCell) { + // @Override + // public String getValue(ContactInfo object) { + // return object.getCategory().getDisplayName(); + // } + // }; + // cellTable.addColumn(categoryColumn, constants.cwCellTableColumnCategory()); + // categoryColumn.setFieldUpdater(new FieldUpdater() { + // public void update(int index, ContactInfo object, String value) { + // for (Category category : categories) { + // if (category.getDisplayName().equals(value)) { + // object.setCategory(category); + // } + // } + // ContactDatabase.get().refreshDisplays(); + // } + // }); + // cellTable.setColumnWidth(categoryColumn, 130, Unit.PX); + + // start date - TODO: provide convenient date & time picker. Not provided by standard gwt and also not even a + // Calender util is available on the client. + // Column startDateColumn = new Column(new DatePickerCell()) { + // @Override + // public Date getValue(MediaTrackDTO mediaTrack) { + // return mediaTrack.startTime; + // } + // }; + // startDateColumn.setSortable(true); + // sortHandler.setComparator(startDateColumn, new Comparator() { + // public int compare(MediaTrackDTO mediaTrack1, MediaTrackDTO mediaTrack2) { + // return mediaTrack1.startTime.compareTo(mediaTrack2.startTime); + // } + // }); + // startDateColumn.setFieldUpdater(new FieldUpdater() { + // public void update(int index, MediaTrackDTO mediaTrack, Date newDate) { + // // Called when the user changes the value. + // calender.setTime(newDate); + // mediaTrack.startTime = newDate; + // mediaTrackListDataProvider.refresh(); + // } + // }); + // mediaTracksTable.addColumn(startDateColumn, stringMessages.startDate()); + // mediaTracksTable.setColumnWidth(startDateColumn, 30, Unit.PCT); + + // start time + Column startTimeColumn = new Column(new EditTextCell()) { + @Override + public String getValue(MediaTrack mediaTrack) { + return DATETIME_FORMAT.format(mediaTrack.startTime); + } + }; + startTimeColumn.setSortable(true); + sortHandler.setComparator(startTimeColumn, new Comparator() { + public int compare(MediaTrack mediaTrack1, MediaTrack mediaTrack2) { + return mediaTrack1.startTime.compareTo(mediaTrack2.startTime); + } + }); + startTimeColumn.setFieldUpdater(new FieldUpdater() { + public void update(int index, MediaTrack mediaTrack, String newStartTime) { + // Called when the user changes the value. + mediaTrack.startTime = DATETIME_FORMAT.parse(newStartTime); + mediaTrackListDataProvider.refresh(); + } + }); + mediaTracksTable.addColumn(startTimeColumn, stringMessages.startTime()); + mediaTracksTable.setColumnWidth(startTimeColumn, 100, Unit.PCT); + + // delete action + Column deleteActionColumn = new IdentityColumn(new ActionCell("X", new ActionCell.Delegate() { + @Override + public void execute(MediaTrack mediaTrack) { + if (Window.confirm(stringMessages.reallyRemoveMediaTrack(mediaTrack.title))) { + removeMediaTrack(mediaTrack); + } + } + })); + mediaTracksTable.addColumn(deleteActionColumn, stringMessages.delete()); + mediaTracksTable.setColumnWidth(deleteActionColumn, 5, Unit.PCT); + + } + + protected void removeMediaTrack(MediaTrack mediaTrack) { + mediaService.deleteMediaTrack(mediaTrack, new AsyncCallback() { + + @Override + public void onFailure(Throwable t) { + errorReporter.reportError(t.toString()); + } + + @Override + public void onSuccess(Void allMediaTracks) { + loadMediaTracks(); + } + }); + } + + private void addMediaTrack() { + MediaTrackDialog dialog = new MediaTrackDialog(stringMessages, new AsyncCallback() { + + @Override + public void onFailure(Throwable arg0) { + // no op + } + + @Override + public void onSuccess(MediaTrack mediaTrack) { + mediaService.addMediaTrack(mediaTrack, new AsyncCallback() { + + @Override + public void onFailure(Throwable t) { + errorReporter.reportError(t.toString()); + } + + @Override + public void onSuccess(Void allMediaTracks) { + loadMediaTracks(); + } + }); + + } + }); + dialog.show(); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java new file mode 100644 index 00000000000..8e1830ebfaa --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java @@ -0,0 +1,122 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.Date; + +import com.google.gwt.event.dom.client.ChangeEvent; +import com.google.gwt.event.dom.client.ChangeHandler; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.TextBox; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.user.datepicker.client.DateBox; +import com.sap.sailing.gwt.ui.client.DataEntryDialog; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaSubType; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaType; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MimeFileType; + +public class MediaTrackDialog extends DataEntryDialog { + + private static final boolean DONT_FIRE_EVENTS = false; + + private static final Validator MEDIA_TRACK_VALIDATOR = new Validator() { + + @Override + public String getErrorMessage(MediaTrack valueToValidate) { + return null; + } + + }; + + private final StringMessages stringMessages; + + private TextBox nameBox; + + private DateBox startTimeBox; + + private TextBox urlBox; + + private TextBox mimeTypeBox; + + public MediaTrackDialog(StringMessages stringMessages, AsyncCallback okCancelCallback) { + super("Media Track", "", stringMessages.ok(), stringMessages.cancel(), MEDIA_TRACK_VALIDATOR, okCancelCallback); + this.stringMessages = stringMessages; + } + + @Override + protected MediaTrack getResult() { + MediaType mediaType; + MediaSubType mediaSubType; + try { + String mimeType = mimeTypeBox.getValue(); + MimeFileType mimeFileType = MimeFileType.valueOf(mimeType); + mediaType = mimeFileType.mediaType; + mediaSubType = mimeFileType.mediaSubType; + } catch (IllegalArgumentException e) { + // ignore. TODO: Somehow put it into the error message. + // throw new IllegalArgumentException("Unsupported media type '" + mimeType + "'.", e); + mediaType = null; + mediaSubType = null; + } + String title = nameBox.getValue(); + String url = urlBox.getValue(); + Date startTime = startTimeBox.getValue(); + MediaTrack result = new MediaTrack(title, url, startTime, mediaType, mediaSubType); + return result; + } + + @Override + protected Widget getAdditionalWidget() { + VerticalPanel mainPanel = new VerticalPanel(); + Grid formGrid = new Grid(4, 2); + formGrid.setCellSpacing(3); + formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":")); + nameBox = createUrlBox(); + formGrid.setWidget(0, 1, nameBox); + formGrid.setWidget(1, 0, new Label(stringMessages.mimeType() + ":")); + mimeTypeBox = createUrlBox(); + formGrid.setWidget(1, 1, mimeTypeBox); + formGrid.setWidget(2, 0, new Label(stringMessages.url() + ":")); + urlBox = createUrlBox(); + formGrid.setWidget(2, 1, urlBox); + formGrid.setWidget(3, 0, new Label(stringMessages.startTime() + ":")); + startTimeBox = createDateBox(new Date().getTime(), 16); + formGrid.setWidget(3, 1, startTimeBox); + mainPanel.add(formGrid); + return mainPanel; + } + + private TextBox createUrlBox() { + TextBox result = createTextBox(null); + result.addChangeHandler(new ChangeHandler() { + + @Override + public void onChange(ChangeEvent event) { + deriveNameAndMimeType(); + } + + }); + return result; + } + + protected void deriveNameAndMimeType() { + String url = urlBox.getValue(); + + String lastPathSegment = url.substring(url.lastIndexOf('/') + 1); + int dotPos = lastPathSegment.lastIndexOf('.'); + String fileEnding = lastPathSegment.substring(dotPos + 1); + String fileName = lastPathSegment.substring(0, dotPos); + + nameBox.setValue(fileName, DONT_FIRE_EVENTS); + mimeTypeBox.setValue(fileEnding, DONT_FIRE_EVENTS); + } + + @Override + public void show() { + super.show(); + urlBox.setFocus(true); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/AbstractEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/AbstractEntryPoint.java index e646393d235..17bcd86074e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/AbstractEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/AbstractEntryPoint.java @@ -26,20 +26,24 @@ public abstract class AbstractEntryPoint implements EntryPoint, ErrorReporter { private Button dialogCloseButton; protected StringMessages stringMessages; protected UserAgentDetails userAgent; - + /** * Create a remote service proxy to talk to the server-side sailing service. */ protected final SailingServiceAsync sailingService = GWT.create(SailingService.class); + /** + * Create a remote service proxy to talk to the server-side media service. + */ + protected final MediaServiceAsync mediaService = GWT.create(MediaService.class); + /** * Create a remote service proxy to talk to the server-side user management service. */ protected final UserManagementServiceAsync userManagementService = GWT.create(UserManagementService.class); /** - * The message displayed to the user when the server cannot be reached or - * returns an error. + * The message displayed to the user when the server cannot be reached or returns an error. */ private static final String SERVER_ERROR = "An error occurred while " //$NON-NLS-1$ + "attempting to contact the server. Please check your network " + "connection and try again."; //$NON-NLS-1$ //$NON-NLS-2$ @@ -47,17 +51,21 @@ public abstract class AbstractEntryPoint implements EntryPoint, ErrorReporter { @Override public void onModuleLoad() { stringMessages = GWT.create(StringMessages.class); - errorDialogBox = createErrorDialog(); /* TODO: Make this more generic (e.g. make it support all kinds of messages) */ + errorDialogBox = createErrorDialog(); /* + * TODO: Make this more generic (e.g. make it support all kinds of + * messages) + */ userAgent = new UserAgentDetails(Window.Navigator.getUserAgent()); - ServiceDefTarget sailingServiceDef = (ServiceDefTarget) sailingService; + ServiceDefTarget mediaServiceDef = (ServiceDefTarget) mediaService; ServiceDefTarget userManagementServiceDef = (ServiceDefTarget) userManagementService; String moduleBaseURL = GWT.getModuleBaseURL(); String baseURL = moduleBaseURL.substring(0, moduleBaseURL.lastIndexOf('/', moduleBaseURL.length()-2)+1); sailingServiceDef.setServiceEntryPoint(baseURL + "sailing"); + mediaServiceDef.setServiceEntryPoint(baseURL + "media"); userManagementServiceDef.setServiceEntryPoint(baseURL + "usermanagement"); } - + @Override public void reportError(String message) { errorDialogBox.setText(message); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaPlayer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaPlayer.java new file mode 100644 index 00000000000..a7f77a0b46f --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaPlayer.java @@ -0,0 +1,19 @@ +package com.sap.sailing.gwt.ui.client; + +import java.util.Date; + +import com.sap.sailing.gwt.ui.shared.media.MediaTrack; + +public interface MediaPlayer { + + boolean isPaused(); + + void pause(); + + void play(); + + void alignTime(Date raceTime); + + void setMediaTrack(MediaTrack mediaTrack); + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java new file mode 100644 index 00000000000..43c4231ecec --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java @@ -0,0 +1,22 @@ +package com.sap.sailing.gwt.ui.client; + +import java.util.Collection; +import java.util.List; + +import com.google.gwt.user.client.rpc.RemoteService; +import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack; + +@RemoteServiceRelativePath("media") +public interface MediaService extends RemoteService { + + Collection getMediaTracksForRace(RegattaAndRaceIdentifier regattaAndRaceIdentifier); + + List getAllMediaTracks(); + + void addMediaTrack(MediaTrack mediaTrack); + + void deleteMediaTrack(MediaTrack mediaTrack); + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java new file mode 100644 index 00000000000..440dfe6a19c --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java @@ -0,0 +1,21 @@ +package com.sap.sailing.gwt.ui.client; + +import java.util.Collection; +import java.util.List; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack; + +public interface MediaServiceAsync { + + void getMediaTracksForRace(RegattaAndRaceIdentifier regattaAndRaceIdentifier, + AsyncCallback> callback); + + void getAllMediaTracks(AsyncCallback> asyncCallback); + + void addMediaTrack(MediaTrack mediaTrack, AsyncCallback asyncCallback); + + void deleteMediaTrack(MediaTrack mediaTrack, AsyncCallback asyncCallback); + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index ee03383f3ec..24712b7e58a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -399,4 +399,9 @@ public interface StringMessages extends Messages { String suppress(); String unsuppress(); String suppressedCompetitors(); + String mediaPanel(); + String url(); + String mimeType(); + String title(); + String reallyRemoveMediaTrack(String mediaTrackTitle); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 2f076ce1c72..27bc5f19aeb 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -398,4 +398,9 @@ latitude=Latitude longitude=Longitude suppress=Suppress unsuppress=Unsuppress -suppressedCompetitors=Suppressed Competitors \ No newline at end of file +suppressedCompetitors=Suppressed Competitors +mediaPanel=Audio & Video +url=URL +mimeType=MIME Type +title=Title +reallyRemoveMediaTrack=Do you really want to remove media track ''{0}''? \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 3d03c0a564e..0cbc07b58b4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -399,4 +399,9 @@ latitude=Breite longitude=Länge suppress=Entfernen unsuppress=Wieder hinzufügen -suppressedCompetitors=Unterdrückte Wettkampfteilnehmer \ No newline at end of file +suppressedCompetitors=Unterdrückte Wettkampfteilnehmer +mediaPanel=Audio & Video +url=URL +mimeType=MIME Type +title=Titel +reallyRemoveMediaTrack=Möchten Sie ''{0}'' wirklich entfernen? \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java index de684ae5c4e..a12f81a0df0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java @@ -24,7 +24,6 @@ import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel; import com.sap.sailing.gwt.ui.client.Timer; import com.sap.sailing.gwt.ui.client.Timer.PlayModes; - public class LeaderboardEntryPoint extends AbstractEntryPoint { private static final Logger logger = Logger.getLogger(LeaderboardEntryPoint.class.getName()); private static final String PARAM_LEADERBOARD_GROUP_NAME = "leaderboardGroupName"; @@ -41,16 +40,16 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint { private static final String PARAM_DELAY_TO_LIVE_MILLIS = "delayToLiveMillis"; private String leaderboardName; private String leaderboardGroupName; - + @Override - public void onModuleLoad() { + public void onModuleLoad() { super.onModuleLoad(); final boolean showRaceDetails = Window.Location.getParameter(PARAM_SHOW_RACE_DETAILS) != null && Window.Location.getParameter(PARAM_SHOW_RACE_DETAILS).equalsIgnoreCase("true"); final boolean embedded = Window.Location.getParameter(PARAM_EMBEDDED) != null && Window.Location.getParameter(PARAM_EMBEDDED).equalsIgnoreCase("true"); - final long delayToLiveMillis = Window.Location.getParameter(PARAM_DELAY_TO_LIVE_MILLIS) != null ? - Long.valueOf(Window.Location.getParameter(PARAM_DELAY_TO_LIVE_MILLIS)) : 5000l; // default 5s + final long delayToLiveMillis = Window.Location.getParameter(PARAM_DELAY_TO_LIVE_MILLIS) != null ? Long + .valueOf(Window.Location.getParameter(PARAM_DELAY_TO_LIVE_MILLIS)) : 5000l; // default 5s sailingService.getLeaderboardNames(new AsyncCallback>() { @Override public void onSuccess(List leaderboardNames) { @@ -69,49 +68,51 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint { } }); } - + private void createUI(boolean showRaceDetails, boolean embedded, long delayToLiveMillis) { DockLayoutPanel mainPanel = new DockLayoutPanel(Unit.PX); RootLayoutPanel.get().add(mainPanel); LogoAndTitlePanel logoAndTitlePanel = null; if (!embedded) { - logoAndTitlePanel = new LogoAndTitlePanel(leaderboardGroupName, - stringMessages.leaderboard() + " " + leaderboardName, stringMessages); + logoAndTitlePanel = new LogoAndTitlePanel(leaderboardGroupName, stringMessages.leaderboard() + " " + + leaderboardName, stringMessages); logoAndTitlePanel.addStyleName("LogoAndTitlePanel"); mainPanel.addNorth(logoAndTitlePanel, 68); } ScrollPanel contentScrollPanel = new ScrollPanel(); - + String tvModeParam = Window.Location.getParameter("tvMode"); if (tvModeParam != null) { Timer timer = new Timer(PlayModes.Live, 1000l); timer.setLivePlayDelayInMillis(delayToLiveMillis); - TVViewPanel tvViewPanel = new TVViewPanel(sailingService, stringMessages, this, leaderboardName, - userAgent, null, timer, logoAndTitlePanel, mainPanel, showRaceDetails); + TVViewPanel tvViewPanel = new TVViewPanel(sailingService, mediaService, stringMessages, this, + leaderboardName, userAgent, null, timer, logoAndTitlePanel, mainPanel, showRaceDetails); contentScrollPanel.setWidget(tvViewPanel); } else { Timer timer = new Timer(PlayModes.Replay, /* delayBetweenAutoAdvancesInMilliseconds */3000l); timer.setLivePlayDelayInMillis(delayToLiveMillis); - final LeaderboardSettings leaderboardSettings = createLeaderboardSettingsFromURLParameters(Window.Location.getParameterMap()); + final LeaderboardSettings leaderboardSettings = createLeaderboardSettingsFromURLParameters(Window.Location + .getParameterMap()); if (leaderboardSettings.getDelayBetweenAutoAdvancesInMilliseconds() != null) { timer.setPlayMode(PlayModes.Live); // the leaderboard, viewed via the entry point, always goes "live" } LeaderboardPanel leaderboardPanel = new LeaderboardPanel(sailingService, new AsyncActionsExecutor(), - leaderboardSettings, - getPreselectedRace(Window.Location.getParameterMap()), new CompetitorSelectionModel( - /* hasMultiSelection */true), timer, leaderboardName, leaderboardGroupName, + leaderboardSettings, getPreselectedRace(Window.Location.getParameterMap()), + new CompetitorSelectionModel( + /* hasMultiSelection */true), timer, leaderboardName, leaderboardGroupName, LeaderboardEntryPoint.this, stringMessages, userAgent, showRaceDetails); contentScrollPanel.setWidget(leaderboardPanel); } mainPanel.add(contentScrollPanel); } - + private RaceIdentifier getPreselectedRace(Map> parameterMap) { RaceIdentifier result; - if (parameterMap.containsKey(PARAM_RACE_NAME) && parameterMap.get(PARAM_RACE_NAME).size() == 1 && - parameterMap.containsKey(PARAM_REGATTA_NAME) && parameterMap.get(PARAM_REGATTA_NAME).size() == 1) { - result = new RegattaNameAndRaceName(parameterMap.get(PARAM_REGATTA_NAME).get(0), parameterMap.get(PARAM_RACE_NAME).get(0)); + if (parameterMap.containsKey(PARAM_RACE_NAME) && parameterMap.get(PARAM_RACE_NAME).size() == 1 + && parameterMap.containsKey(PARAM_REGATTA_NAME) && parameterMap.get(PARAM_REGATTA_NAME).size() == 1) { + result = new RegattaNameAndRaceName(parameterMap.get(PARAM_REGATTA_NAME).get(0), parameterMap.get( + PARAM_RACE_NAME).get(0)); } else { result = null; } @@ -158,7 +159,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint { try { result.add(DetailType.valueOf(entry)); } catch (IllegalArgumentException e) { - logger.info("Can't find detail type "+entry+". Ignoring."); + logger.info("Can't find detail type " + entry + ". Ignoring."); } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/TVViewPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/TVViewPanel.java index ef0f88d276e..f598d51f8ba 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/TVViewPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/TVViewPanel.java @@ -16,6 +16,7 @@ import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor; import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel; +import com.sap.sailing.gwt.ui.client.MediaServiceAsync; import com.sap.sailing.gwt.ui.client.RaceSelectionModel; import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider; import com.sap.sailing.gwt.ui.client.RaceTimesInfoProviderListener; @@ -33,36 +34,40 @@ import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO; import com.sap.sailing.gwt.ui.shared.UserDTO; public class TVViewPanel extends SimplePanel implements RaceTimesInfoProviderListener { - + private final SailingServiceAsync sailingService; + private final MediaServiceAsync mediaService; private final StringMessages stringMessages; private final ErrorReporter errorReporter; private final UserAgentDetails userAgent; private final UserDTO userDTO; private final LogoAndTitlePanel logoAndTitlePanel; private final DockLayoutPanel dockPanel; - + private final Timer timer; private RaceTimesInfoProvider raceTimesInfoProvider; - + private LeaderboardPanel leaderboardPanel; private LeaderboardDTO leaderboard; private boolean leaderboardIsWiget; - + private Label raceBoardHeader; private RaceBoardPanel raceBoardPanel; private FlowPanel timePanel; private RegattaAndRaceIdentifier currentRace; private boolean raceBoardIsWidget; - + /** - * @param logoAndTitlePanel allowed to be null + * @param logoAndTitlePanel + * allowed to be null */ - public TVViewPanel(SailingServiceAsync sailingService, StringMessages stringMessages, ErrorReporter errorReporter, - String leaderboardName, UserAgentDetails userAgent, UserDTO userDTO, Timer timer, - LogoAndTitlePanel logoAndTitlePanel, DockLayoutPanel dockPanel, boolean showRaceDetails) { + public TVViewPanel(SailingServiceAsync sailingService, MediaServiceAsync mediaService, + StringMessages stringMessages, ErrorReporter errorReporter, String leaderboardName, + UserAgentDetails userAgent, UserDTO userDTO, Timer timer, LogoAndTitlePanel logoAndTitlePanel, + DockLayoutPanel dockPanel, boolean showRaceDetails) { setSize("100%", "100%"); this.sailingService = sailingService; + this.mediaService = mediaService; this.stringMessages = stringMessages; this.errorReporter = errorReporter; this.userAgent = userAgent; @@ -74,18 +79,20 @@ public class TVViewPanel extends SimplePanel implements RaceTimesInfoProviderLis raceBoardIsWidget = false; raceBoardPanel = null; timePanel = null; - + timer.play(); - raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, errorReporter, new ArrayList(), 1000l); + raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, errorReporter, + new ArrayList(), 1000l); raceTimesInfoProvider.addRaceTimesInfoProviderListener(this); leaderboardPanel = createLeaderboardPanel(leaderboardName, showRaceDetails); leaderboard = null; showLeaderboard(); } - + private LeaderboardPanel createLeaderboardPanel(String leaderboardName, boolean showRaceDetails) { - LeaderboardSettings settings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false); - CompetitorSelectionModel selectionModel = new CompetitorSelectionModel(/* hasMultiSelection */ true); + LeaderboardSettings settings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, + null, /* autoExpandFirstRace */false); + CompetitorSelectionModel selectionModel = new CompetitorSelectionModel(/* hasMultiSelection */true); Timer timer = new Timer(PlayModes.Live, /* delayBetweenAutoAdvancesInMilliseconds */3000l); LeaderboardPanel leaderboardPanel = new LeaderboardPanel(sailingService, new AsyncActionsExecutor(), settings, /* preSelectedRace */null, selectionModel, timer, leaderboardName, null, errorReporter, stringMessages, @@ -99,7 +106,7 @@ public class TVViewPanel extends SimplePanel implements RaceTimesInfoProviderLis }; return leaderboardPanel; } - + private void updateRaceTimesInfoProvider() { boolean providerChanged = false; for (RaceColumnDTO race : leaderboard.getRaceList()) { @@ -115,20 +122,21 @@ public class TVViewPanel extends SimplePanel implements RaceTimesInfoProviderLis raceTimesInfoProvider.forceTimesInfosUpdate(); } } - + private RaceBoardPanel createRaceBoardPanel(String leaderboardName, RegattaAndRaceIdentifier raceToShow) { RaceSelectionModel raceSelectionModel = new RaceSelectionModel(); List singletonList = Collections.singletonList(raceToShow); raceSelectionModel.setSelection(singletonList); - RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, userDTO, timer, raceSelectionModel, leaderboardName, null, - errorReporter, stringMessages, userAgent, RaceBoardViewModes.ONESCREEN, raceTimesInfoProvider); + RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, userDTO, timer, + raceSelectionModel, leaderboardName, null, errorReporter, stringMessages, userAgent, + RaceBoardViewModes.ONESCREEN, raceTimesInfoProvider); return raceBoardPanel; } - + private void showLeaderboard() { if (!leaderboardIsWiget) { if (leaderboard != null) { - //Resetting the settings of the leaderboard panel to prevent, that some race columns get lost + // Resetting the settings of the leaderboard panel to prevent, that some race columns get lost List namesOfRaceColumnsToShow = new ArrayList(); for (RaceColumnDTO race : leaderboard.getRaceList()) { namesOfRaceColumnsToShow.add(race.getRaceColumnName()); @@ -137,7 +145,7 @@ public class TVViewPanel extends SimplePanel implements RaceTimesInfoProviderLis namesOfRaceColumnsToShow, null, null, false); leaderboardPanel.updateSettings(settings); } - + setWidget(leaderboardPanel); if (raceBoardPanel != null) { if (logoAndTitlePanel != null) { @@ -148,12 +156,12 @@ public class TVViewPanel extends SimplePanel implements RaceTimesInfoProviderLis raceBoardPanel = null; } currentRace = null; - + leaderboardIsWiget = true; raceBoardIsWidget = false; } } - + private void showRaceBoard() { if (!raceBoardIsWidget) { if (logoAndTitlePanel != null) { @@ -164,10 +172,10 @@ public class TVViewPanel extends SimplePanel implements RaceTimesInfoProviderLis if (logoAndTitlePanel != null) { logoAndTitlePanel.add(raceBoardHeader); } - + timePanel = createTimePanel(); dockPanel.insertSouth(timePanel, 122, dockPanel.getWidget(0)); - + Scheduler.get().scheduleFixedDelay(new RepeatingCommand() { @Override public boolean execute() { @@ -182,9 +190,9 @@ public class TVViewPanel extends SimplePanel implements RaceTimesInfoProviderLis } }, 1000); setWidget(raceBoardPanel); - //Setting the size or the race board wouldn't be displayed + // Setting the size or the race board wouldn't be displayed raceBoardPanel.setSize("100%", "100%"); - + raceBoardIsWidget = true; leaderboardIsWiget = false; } @@ -194,18 +202,18 @@ public class TVViewPanel extends SimplePanel implements RaceTimesInfoProviderLis FlowPanel timeLineInnerBgPanel = new FlowPanel(); timeLineInnerBgPanel.addStyleName("timeLineInnerBgPanel"); timeLineInnerBgPanel.add(raceBoardPanel.getTimeWidget()); - + FlowPanel timeLineInnerPanel = new FlowPanel(); timeLineInnerPanel.add(timeLineInnerBgPanel); timeLineInnerPanel.addStyleName("timeLineInnerPanel"); - + FlowPanel timelinePanel = new FlowPanel(); timelinePanel.add(timeLineInnerPanel); timelinePanel.addStyleName("timeLinePanel"); - + return timelinePanel; } - + @Override public void raceTimesInfosReceived(Map raceTimesInfo) { if (currentRace == null) { @@ -218,14 +226,14 @@ public class TVViewPanel extends SimplePanel implements RaceTimesInfoProviderLis } } else { RaceTimesInfoDTO currentRaceTimes = raceTimesInfo.get(currentRace); - //TODO add check for live mode + // TODO add check for live mode if (currentRaceTimes.endOfRace != null && timer.getTime().after(currentRaceTimes.endOfRace) && timer.getPlayMode() == PlayModes.Live) { showLeaderboard(); } } } - + private RegattaAndRaceIdentifier getFirstStartedAndUnfinishedRace() { RegattaAndRaceIdentifier firstStartedAndUnfinishedRace = null; Map raceTimesInfos = raceTimesInfoProvider.getRaceTimesInfos(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java new file mode 100644 index 00000000000..1ab03448134 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java @@ -0,0 +1,133 @@ +package com.sap.sailing.gwt.ui.raceboard; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Map; + +import com.google.gwt.dom.client.Style; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.CheckBox; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.domain.common.RaceIdentifier; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.gwt.ui.client.ErrorReporter; +import com.sap.sailing.gwt.ui.client.MediaPlayer; +import com.sap.sailing.gwt.ui.client.PlayStateListener; +import com.sap.sailing.gwt.ui.client.RaceTimesInfoProviderListener; +import com.sap.sailing.gwt.ui.client.TimeListener; +import com.sap.sailing.gwt.ui.client.Timer.PlayModes; +import com.sap.sailing.gwt.ui.client.Timer.PlayStates; +import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack; + +public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateListener, TimeListener, + AsyncCallback> { + + private final CheckBox toggleVideoPanel; + private final Collection activePlayers = new ArrayList(); + private final Collection audioTracks = new ArrayList(); + private final Collection videoTracks = new ArrayList(); + private final ErrorReporter errorReporter; + + public MediaSelector(ErrorReporter errorReporter) { + this.errorReporter = errorReporter; + + final VideoPopup videoPopup = new VideoPopup(); + activePlayers.add(videoPopup); + + toggleVideoPanel = new CheckBox("Show Video"); + toggleVideoPanel.setEnabled(false); + toggleVideoPanel.addStyleName("raceBoardNavigation-innerElement"); + toggleVideoPanel.getElement().getStyle().setFloat(Style.Float.LEFT); + toggleVideoPanel.addValueChangeHandler(new ValueChangeHandler() { + + @Override + public void onValueChange(ValueChangeEvent event) { + + if (toggleVideoPanel.getValue()) { + videoPopup.show(); + } else { + videoPopup.hide(); + } + + } + }); + } + + @Override + public void playStateChanged(PlayStates playState, PlayModes playMode) { + if (PlayModes.Replay.equals(playMode)) { + switch (playState) { + case Playing: + startPlaying(); + break; + case Paused: + case Stopped: + pausePlaying(); + default: + break; + } + } else { + // TODO + } + } + + private void pausePlaying() { + for (MediaPlayer player : activePlayers) { + if (!player.isPaused()) { + player.pause(); + } + } + } + + private void startPlaying() { + for (MediaPlayer player : activePlayers) { + if (player.isPaused()) { + player.play(); + } + } + } + + @Override + public void timeChanged(Date raceTime) { + for (MediaPlayer player : activePlayers) { + player.alignTime(raceTime); + } + } + + public Widget widget() { + return toggleVideoPanel; + } + + @Override + public void onFailure(Throwable caught) { + errorReporter + .reportError("Remote Procedure Call getPreviousConfigurations() - Failure: " + caught.getMessage()); + } + + @Override + public void onSuccess(Collection mediaTracks) { + toggleVideoPanel.setEnabled(mediaTracks.size() > 0); + for (MediaTrack mediaTrack : mediaTracks) { + switch (mediaTrack.type) { + case AUDIO: + audioTracks.add(mediaTrack); + break; + case VIDEO: + videoTracks.add(mediaTrack); + activePlayers.iterator().next().setMediaTrack(mediaTrack); + break; + } + } + } + + @Override + public void raceTimesInfosReceived(Map raceTimesInfo) { + // TODO Auto-generated method stub + + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index 3c25bf890a5..05055e54405 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -1,199 +1,208 @@ - -package com.sap.sailing.gwt.ui.raceboard; - -import java.util.Collections; -import java.util.List; - -import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.DockLayoutPanel; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.RootLayoutPanel; -import com.sap.sailing.domain.common.LeaderboardNameConstants; -import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; -import com.sap.sailing.gwt.ui.client.AbstractEntryPoint; -import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel; -import com.sap.sailing.gwt.ui.client.ParallelExecutionCallback; -import com.sap.sailing.gwt.ui.client.ParallelExecutionHolder; -import com.sap.sailing.gwt.ui.client.RaceSelectionModel; -import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider; -import com.sap.sailing.gwt.ui.client.Timer; -import com.sap.sailing.gwt.ui.client.Timer.PlayModes; -import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO; -import com.sap.sailing.gwt.ui.shared.RaceDTO; -import com.sap.sailing.gwt.ui.shared.RegattaDTO; -import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; -import com.sap.sailing.gwt.ui.shared.UserDTO; - -public class RaceBoardEntryPoint extends AbstractEntryPoint { - private RaceDTO selectedRace; - - private String regattaName; - private String raceName; - private String leaderboardName; - private String leaderboardGroupName; - private RaceBoardViewModes viewMode; - - @Override - public void onModuleLoad() { - super.onModuleLoad(); - regattaName = Window.Location.getParameter("regattaName"); - raceName = Window.Location.getParameter("raceName"); - String leaderboardNameParamValue = Window.Location.getParameter("leaderboardName"); - String leaderboardGroupNameParamValue = Window.Location.getParameter("leaderboardGroupName"); - String viewModeParamValue = Window.Location.getParameter("viewMode"); - // set the view mode for the race board - if (viewModeParamValue != null && !viewModeParamValue.isEmpty()) { - try { - viewMode = RaceBoardViewModes.valueOf(viewModeParamValue); - } catch (IllegalArgumentException e) { - viewMode = RaceBoardViewModes.ONESCREEN; - } - } else { - viewMode = RaceBoardViewModes.ONESCREEN; - } - if (leaderboardNameParamValue == null || leaderboardNameParamValue.isEmpty()) { - leaderboardName = LeaderboardNameConstants.DEFAULT_LEADERBOARD_NAME; - } else { - leaderboardName = leaderboardNameParamValue; - } - if (leaderboardGroupNameParamValue != null && !leaderboardGroupNameParamValue.isEmpty()) { - leaderboardGroupName = leaderboardGroupNameParamValue; - } - if (regattaName == null || regattaName.isEmpty() || raceName == null || raceName.isEmpty()) { - createErrorPage("This page requires a valid regatta name and race name."); - return; - } - final ParallelExecutionCallback> getLeaderboardNamesCallback = new ParallelExecutionCallback>(); - final ParallelExecutionCallback> getRegattasCallback = new ParallelExecutionCallback>(); - final ParallelExecutionCallback getLeaderboardGroupByNameCallback = new ParallelExecutionCallback(); - final ParallelExecutionCallback getUserCallback = new ParallelExecutionCallback(); - if (leaderboardGroupName != null) { - new ParallelExecutionHolder(getLeaderboardNamesCallback, getLeaderboardGroupByNameCallback, getRegattasCallback, getUserCallback) { - @Override - public void handleSuccess() { - checkUrlParameters(getLeaderboardNamesCallback.getData(), - getLeaderboardGroupByNameCallback.getData(), getRegattasCallback.getData(), getUserCallback.getData()); - } - @Override - public void handleFailure(Throwable t) { - reportError("Error trying to create the raceboard: " + t.getMessage()); - } - }; - } else { - new ParallelExecutionHolder(getLeaderboardNamesCallback, getRegattasCallback, getUserCallback) { - @Override - public void handleSuccess() { - checkUrlParameters(getLeaderboardNamesCallback.getData(), null, getRegattasCallback.getData(), getUserCallback.getData()); - } - @Override - public void handleFailure(Throwable t) { - reportError("Error trying to create the raceboard: " + t.getMessage()); - } - }; - } - sailingService.getRegattas(getRegattasCallback); - sailingService.getLeaderboardNames(getLeaderboardNamesCallback); - if (leaderboardGroupName != null) { - sailingService.getLeaderboardGroupByName(leaderboardGroupNameParamValue, false /*withGeoLocationData*/, getLeaderboardGroupByNameCallback); - } - userManagementService.getUser(getUserCallback); - } - - private void checkUrlParameters(List leaderboardNames, LeaderboardGroupDTO leaderboardGroup, List regattas, UserDTO user) { - if (!leaderboardNames.contains(leaderboardName)) { - createErrorPage(stringMessages.noSuchLeaderboard()); - return; - } - if (leaderboardGroupName != null && leaderboardGroup != null) { - boolean foundLeaderboard = false; - for(StrippedLeaderboardDTO leaderBoard: leaderboardGroup.leaderboards) { - if(leaderBoard.name.equals(leaderboardName)) { - foundLeaderboard = true; - break; - } - } - if(!foundLeaderboard) { - createErrorPage("the leaderboard is not contained in this leaderboard group."); - return; - } - } - selectedRace = findRace(regattaName, raceName, regattas); - if (selectedRace == null) { - createErrorPage("Could not obtain a race with name " + raceName + " for a regatta with name " + regattaName); - return; - } - - RaceSelectionModel raceSelectionModel = new RaceSelectionModel(); - List singletonList = Collections.singletonList(selectedRace.getRaceIdentifier()); - raceSelectionModel.setSelection(singletonList); - Timer timer = new Timer(PlayModes.Replay, 1000l); - RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, this, singletonList, 5000l /* requestInterval*/); - RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, user, timer, raceSelectionModel, leaderboardName, leaderboardGroupName, - RaceBoardEntryPoint.this, stringMessages, userAgent, viewMode, raceTimesInfoProvider); - raceBoardPanel.fillRegattas(regattas); - - switch (viewMode) { - case ONESCREEN: - createRaceBoardInOneScreenMode(raceBoardPanel); - break; - } - } - - private RaceDTO findRace(String regattaName, String raceName, List regattas) { - for (RegattaDTO regattaDTO : regattas) { - if (regattaDTO.name.equals(regattaName)) { - for (RaceDTO raceDTO : regattaDTO.races) { - if (raceDTO.name.equals(raceName)) { - return raceDTO; - } - } - } - } - return null; - } - - private FlowPanel createTimePanel(RaceBoardPanel raceBoardPanel) { - FlowPanel timeLineInnerBgPanel = new FlowPanel(); - timeLineInnerBgPanel.addStyleName("timeLineInnerBgPanel"); - timeLineInnerBgPanel.add(raceBoardPanel.getTimeWidget()); - - FlowPanel timeLineInnerPanel = new FlowPanel(); - timeLineInnerPanel.add(timeLineInnerBgPanel); - timeLineInnerPanel.addStyleName("timeLineInnerPanel"); - - FlowPanel timelinePanel = new FlowPanel(); - timelinePanel.add(timeLineInnerPanel); - timelinePanel.addStyleName("timeLinePanel"); - - return timelinePanel; - } - - private FlowPanel createLogoAndTitlePanel(RaceBoardPanel raceBoardPanel) { - LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(regattaName, selectedRace.name, stringMessages); - logoAndTitlePanel.addStyleName("LogoAndTitlePanel"); - - FlowPanel globalNavigationPanel = new GlobalNavigationPanel(stringMessages, true, leaderboardName, leaderboardGroupName); - logoAndTitlePanel.add(globalNavigationPanel); - - return logoAndTitlePanel; - } - - private void createRaceBoardInOneScreenMode(RaceBoardPanel raceBoardPanel) { - DockLayoutPanel p = new DockLayoutPanel(Unit.PX); - RootLayoutPanel.get().add(p); - - FlowPanel toolbarPanel = new FlowPanel(); - - toolbarPanel.add(raceBoardPanel.getNavigationWidget()); - - FlowPanel logoAndTitlePanel = createLogoAndTitlePanel(raceBoardPanel); - FlowPanel timePanel = createTimePanel(raceBoardPanel); - - p.addNorth(logoAndTitlePanel, 68); - p.addNorth(toolbarPanel, 40); - p.addSouth(timePanel, 90); - p.add(raceBoardPanel); - p.addStyleName("dockLayoutPanel"); - } -} +package com.sap.sailing.gwt.ui.raceboard; + +import java.util.Collections; +import java.util.List; + +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.DockLayoutPanel; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.RootLayoutPanel; +import com.sap.sailing.domain.common.LeaderboardNameConstants; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.gwt.ui.client.AbstractEntryPoint; +import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel; +import com.sap.sailing.gwt.ui.client.ParallelExecutionCallback; +import com.sap.sailing.gwt.ui.client.ParallelExecutionHolder; +import com.sap.sailing.gwt.ui.client.RaceSelectionModel; +import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider; +import com.sap.sailing.gwt.ui.client.Timer; +import com.sap.sailing.gwt.ui.client.Timer.PlayModes; +import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO; +import com.sap.sailing.gwt.ui.shared.RaceDTO; +import com.sap.sailing.gwt.ui.shared.RegattaDTO; +import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; +import com.sap.sailing.gwt.ui.shared.UserDTO; + +public class RaceBoardEntryPoint extends AbstractEntryPoint { + private RaceDTO selectedRace; + + private String regattaName; + private String raceName; + private String leaderboardName; + private String leaderboardGroupName; + private RaceBoardViewModes viewMode; + + @Override + public void onModuleLoad() { + super.onModuleLoad(); + regattaName = Window.Location.getParameter("regattaName"); + raceName = Window.Location.getParameter("raceName"); + String leaderboardNameParamValue = Window.Location.getParameter("leaderboardName"); + String leaderboardGroupNameParamValue = Window.Location.getParameter("leaderboardGroupName"); + String viewModeParamValue = Window.Location.getParameter("viewMode"); + // set the view mode for the race board + if (viewModeParamValue != null && !viewModeParamValue.isEmpty()) { + try { + viewMode = RaceBoardViewModes.valueOf(viewModeParamValue); + } catch (IllegalArgumentException e) { + viewMode = RaceBoardViewModes.ONESCREEN; + } + } else { + viewMode = RaceBoardViewModes.ONESCREEN; + } + if (leaderboardNameParamValue == null || leaderboardNameParamValue.isEmpty()) { + leaderboardName = LeaderboardNameConstants.DEFAULT_LEADERBOARD_NAME; + } else { + leaderboardName = leaderboardNameParamValue; + } + if (leaderboardGroupNameParamValue != null && !leaderboardGroupNameParamValue.isEmpty()) { + leaderboardGroupName = leaderboardGroupNameParamValue; + } + if (regattaName == null || regattaName.isEmpty() || raceName == null || raceName.isEmpty()) { + createErrorPage("This page requires a valid regatta name and race name."); + return; + } + final ParallelExecutionCallback> getLeaderboardNamesCallback = new ParallelExecutionCallback>(); + final ParallelExecutionCallback> getRegattasCallback = new ParallelExecutionCallback>(); + final ParallelExecutionCallback getLeaderboardGroupByNameCallback = new ParallelExecutionCallback(); + final ParallelExecutionCallback getUserCallback = new ParallelExecutionCallback(); + if (leaderboardGroupName != null) { + new ParallelExecutionHolder(getLeaderboardNamesCallback, getLeaderboardGroupByNameCallback, + getRegattasCallback, getUserCallback) { + @Override + public void handleSuccess() { + checkUrlParameters(getLeaderboardNamesCallback.getData(), + getLeaderboardGroupByNameCallback.getData(), getRegattasCallback.getData(), + getUserCallback.getData()); + } + + @Override + public void handleFailure(Throwable t) { + reportError("Error trying to create the raceboard: " + t.getMessage()); + } + }; + } else { + new ParallelExecutionHolder(getLeaderboardNamesCallback, getRegattasCallback, getUserCallback) { + @Override + public void handleSuccess() { + checkUrlParameters(getLeaderboardNamesCallback.getData(), null, getRegattasCallback.getData(), + getUserCallback.getData()); + } + + @Override + public void handleFailure(Throwable t) { + reportError("Error trying to create the raceboard: " + t.getMessage()); + } + }; + } + sailingService.getRegattas(getRegattasCallback); + sailingService.getLeaderboardNames(getLeaderboardNamesCallback); + if (leaderboardGroupName != null) { + sailingService.getLeaderboardGroupByName(leaderboardGroupNameParamValue, false /* withGeoLocationData */, + getLeaderboardGroupByNameCallback); + } + userManagementService.getUser(getUserCallback); + } + + private void checkUrlParameters(List leaderboardNames, LeaderboardGroupDTO leaderboardGroup, + List regattas, UserDTO user) { + if (!leaderboardNames.contains(leaderboardName)) { + createErrorPage(stringMessages.noSuchLeaderboard()); + return; + } + if (leaderboardGroupName != null && leaderboardGroup != null) { + boolean foundLeaderboard = false; + for (StrippedLeaderboardDTO leaderBoard : leaderboardGroup.leaderboards) { + if (leaderBoard.name.equals(leaderboardName)) { + foundLeaderboard = true; + break; + } + } + if (!foundLeaderboard) { + createErrorPage("the leaderboard is not contained in this leaderboard group."); + return; + } + } + selectedRace = findRace(regattaName, raceName, regattas); + if (selectedRace == null) { + createErrorPage("Could not obtain a race with name " + raceName + " for a regatta with name " + regattaName); + return; + } + + RaceSelectionModel raceSelectionModel = new RaceSelectionModel(); + List singletonList = Collections.singletonList(selectedRace.getRaceIdentifier()); + raceSelectionModel.setSelection(singletonList); + Timer timer = new Timer(PlayModes.Replay, 1000l); + RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, this, singletonList, + 5000l /* requestInterval */); + RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, user, timer, + raceSelectionModel, leaderboardName, leaderboardGroupName, RaceBoardEntryPoint.this, stringMessages, + userAgent, viewMode, raceTimesInfoProvider); + raceBoardPanel.fillRegattas(regattas); + + switch (viewMode) { + case ONESCREEN: + createRaceBoardInOneScreenMode(raceBoardPanel); + break; + } + } + + private RaceDTO findRace(String regattaName, String raceName, List regattas) { + for (RegattaDTO regattaDTO : regattas) { + if (regattaDTO.name.equals(regattaName)) { + for (RaceDTO raceDTO : regattaDTO.races) { + if (raceDTO.name.equals(raceName)) { + return raceDTO; + } + } + } + } + return null; + } + + private FlowPanel createTimePanel(RaceBoardPanel raceBoardPanel) { + FlowPanel timeLineInnerBgPanel = new FlowPanel(); + timeLineInnerBgPanel.addStyleName("timeLineInnerBgPanel"); + timeLineInnerBgPanel.add(raceBoardPanel.getTimeWidget()); + + FlowPanel timeLineInnerPanel = new FlowPanel(); + timeLineInnerPanel.add(timeLineInnerBgPanel); + timeLineInnerPanel.addStyleName("timeLineInnerPanel"); + + FlowPanel timelinePanel = new FlowPanel(); + timelinePanel.add(timeLineInnerPanel); + timelinePanel.addStyleName("timeLinePanel"); + + return timelinePanel; + } + + private FlowPanel createLogoAndTitlePanel(RaceBoardPanel raceBoardPanel) { + LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(regattaName, selectedRace.name, stringMessages); + logoAndTitlePanel.addStyleName("LogoAndTitlePanel"); + + FlowPanel globalNavigationPanel = new GlobalNavigationPanel(stringMessages, true, leaderboardName, + leaderboardGroupName); + logoAndTitlePanel.add(globalNavigationPanel); + + return logoAndTitlePanel; + } + + private void createRaceBoardInOneScreenMode(RaceBoardPanel raceBoardPanel) { + DockLayoutPanel p = new DockLayoutPanel(Unit.PX); + RootLayoutPanel.get().add(p); + + FlowPanel toolbarPanel = new FlowPanel(); + + toolbarPanel.add(raceBoardPanel.getNavigationWidget()); + + FlowPanel logoAndTitlePanel = createLogoAndTitlePanel(raceBoardPanel); + FlowPanel timePanel = createTimePanel(raceBoardPanel); + + p.addNorth(logoAndTitlePanel, 68); + p.addNorth(toolbarPanel, 40); + p.addSouth(timePanel, 90); + p.add(raceBoardPanel); + p.addStyleName("dockLayoutPanel"); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index ab7e64c5d18..db596313f72 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -25,6 +25,7 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor; import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.ErrorReporter; +import com.sap.sailing.gwt.ui.client.MediaServiceAsync; import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener; import com.sap.sailing.gwt.ui.client.RaceSelectionProvider; import com.sap.sailing.gwt.ui.client.RaceTimePanel; @@ -51,26 +52,28 @@ import com.sap.sailing.gwt.ui.shared.components.SettingsDialog; import com.sap.sailing.gwt.ui.shared.racemap.RaceMap; /** - * A panel showing a list of components visualizing a race from the regattas announced by calls to {@link #fillRegattas(List)}. - * The race selection is provided by a {@link RaceSelectionProvider} for which this is a {@link RaceSelectionChangeListener listener}. - * {@link RaceIdentifier}-based race selection changes are converted to {@link RaceDTO} objects using the {@link #racesByIdentifier} - * map maintained during {@link #fillRegattas(List)}. The race selection provider is expected to be single selection only. + * A panel showing a list of components visualizing a race from the regattas announced by calls to + * {@link #fillRegattas(List)}. The race selection is provided by a {@link RaceSelectionProvider} for which this is a + * {@link RaceSelectionChangeListener listener}. {@link RaceIdentifier}-based race selection changes are converted to + * {@link RaceDTO} objects using the {@link #racesByIdentifier} map maintained during {@link #fillRegattas(List)}. The + * race selection provider is expected to be single selection only. * * @author Frank Mittag, Axel Uhl (d043530) - * + * */ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceSelectionChangeListener { private final SailingServiceAsync sailingService; + private final MediaServiceAsync mediaService; private final StringMessages stringMessages; private final ErrorReporter errorReporter; private String raceBoardName; private RaceBoardViewModes viewMode; - + /** * Updated upon each {@link #fillRegattas(List)} */ private final Map racesByIdentifier; - + /** * The offset when scrolling with the menu entry anchors (in the top right corner). */ @@ -84,27 +87,28 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS private final RaceSelectionProvider raceSelectionProvider; private final UserAgentDetails userAgent; private final CompetitorSelectionModel competitorSelectionModel; - private final TimeZoomModel timeZoomModel; + private final TimeZoomModel timeZoomModel; private final RegattaAndRaceIdentifier selectedRaceIdentifier; private LeaderboardPanel leaderboardPanel; private WindChart windChart; private MultiChartPanel competitorChart; - + /** * The component viewer in ONESCREEN view mode. null if in CASCADE view mode */ private SideBySideComponentViewer leaderboardAndMapViewer; private final AsyncActionsExecutor asyncActionsExecutor; - + private final RaceTimesInfoProvider raceTimesInfoProvider; - public RaceBoardPanel(SailingServiceAsync sailingService, UserDTO theUser, Timer timer, - RaceSelectionProvider theRaceSelectionProvider, String leaderboardName, String leaderboardGroupName, - ErrorReporter errorReporter, final StringMessages stringMessages, UserAgentDetails userAgent, - RaceBoardViewModes viewMode, RaceTimesInfoProvider raceTimesInfoProvider) { + public RaceBoardPanel(SailingServiceAsync sailingService, MediaServiceAsync mediaService, UserDTO theUser, + Timer timer, RaceSelectionProvider theRaceSelectionProvider, String leaderboardName, + String leaderboardGroupName, ErrorReporter errorReporter, final StringMessages stringMessages, + UserAgentDetails userAgent, RaceBoardViewModes viewMode, RaceTimesInfoProvider raceTimesInfoProvider) { this.sailingService = sailingService; + this.mediaService = mediaService; this.stringMessages = stringMessages; this.raceSelectionProvider = theRaceSelectionProvider; this.raceTimesInfoProvider = raceTimesInfoProvider; @@ -125,17 +129,17 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS this.timer = timer; timeZoomModel = new TimeZoomModel(); componentViewers = new ArrayList(); - competitorSelectionModel = new CompetitorSelectionModel(/* hasMultiSelection */ true); + competitorSelectionModel = new CompetitorSelectionModel(/* hasMultiSelection */true); componentsNavigationPanel = new FlowPanel(); componentsNavigationPanel.addStyleName("raceBoardNavigation"); switch (this.viewMode) { - case ONESCREEN: - createOneScreenView(leaderboardName, leaderboardGroupName, mainPanel); - getElement().getStyle().setMarginLeft(12, Unit.PX); - getElement().getStyle().setMarginRight(12, Unit.PX); - break; + case ONESCREEN: + createOneScreenView(leaderboardName, leaderboardGroupName, mainPanel); + getElement().getStyle().setMarginLeft(12, Unit.PX); + getElement().getStyle().setMarginRight(12, Unit.PX); + break; } timePanel = new RaceTimePanel(timer, stringMessages, raceTimesInfoProvider); @@ -144,67 +148,76 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS raceSelectionProvider.addRaceSelectionChangeListener(timePanel); timePanel.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces()); } - + private void createOneScreenView(String leaderboardName, String leaderboardGroupName, FlowPanel mainPanel) { // create the default leaderboard and select the right race leaderboardPanel = createLeaderboardPanel(leaderboardName, leaderboardGroupName); - RaceMap raceMap = new RaceMap(sailingService, asyncActionsExecutor, errorReporter, timer, competitorSelectionModel, stringMessages); + RaceMap raceMap = new RaceMap(sailingService, asyncActionsExecutor, errorReporter, timer, + competitorSelectionModel, stringMessages); raceTimesInfoProvider.addRaceTimesInfoProviderListener(raceMap); raceMap.onRaceSelectionChange(Collections.singletonList(selectedRaceIdentifier)); List> components = new ArrayList>(); - competitorChart = new MultiChartPanel(sailingService, asyncActionsExecutor, competitorSelectionModel, raceSelectionProvider, - timer, timeZoomModel, stringMessages, errorReporter, true, true); + competitorChart = new MultiChartPanel(sailingService, asyncActionsExecutor, competitorSelectionModel, + raceSelectionProvider, timer, timeZoomModel, stringMessages, errorReporter, true, true); competitorChart.setVisible(false); raceTimesInfoProvider.addRaceTimesInfoProviderListener(competitorChart); competitorChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces()); components.add(competitorChart); windChart = new WindChart(sailingService, raceSelectionProvider, timer, timeZoomModel, new WindChartSettings(), - stringMessages, asyncActionsExecutor, errorReporter, /* compactChart */ true); + stringMessages, asyncActionsExecutor, errorReporter, /* compactChart */true); windChart.setVisible(false); raceTimesInfoProvider.addRaceTimesInfoProviderListener(windChart); windChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces()); components.add(windChart); - - leaderboardAndMapViewer = new SideBySideComponentViewer(leaderboardPanel, raceMap, components); + + leaderboardAndMapViewer = new SideBySideComponentViewer(leaderboardPanel, raceMap, components); componentViewers.add(leaderboardAndMapViewer); - + for (ComponentViewer componentViewer : componentViewers) { mainPanel.add(componentViewer.getViewerWidget()); } addComponentToNavigationMenu(leaderboardAndMapViewer, leaderboardPanel, true); - addComponentToNavigationMenu(leaderboardAndMapViewer, windChart, true); + addComponentToNavigationMenu(leaderboardAndMapViewer, windChart, true); addComponentToNavigationMenu(leaderboardAndMapViewer, competitorChart, true); addComponentToNavigationMenu(leaderboardAndMapViewer, raceMap, false); + + MediaSelector mediaSelector = new MediaSelector(errorReporter); + raceTimesInfoProvider.addRaceTimesInfoProviderListener(mediaSelector); + timer.addPlayStateListener(mediaSelector); + timer.addTimeListener(mediaSelector); + mediaService.getMediaTracksForRace(selectedRaceIdentifier, mediaSelector); + + componentsNavigationPanel.add(mediaSelector.widget()); } @SuppressWarnings("unused") private void addSettingsMenuItem(MenuBar settingsMenu, final Component component) { - if(component.hasSettings()) { + if (component.hasSettings()) { settingsMenu.addItem(component.getLocalizedShortName(), new Command() { public void execute() { new SettingsDialog(component, stringMessages).show(); - } + } }); } } - + private LeaderboardPanel createLeaderboardPanel(String leaderboardName, String leaderboardGroupName) { LeaderboardSettings leaderBoardSettings = LeaderboardSettingsFactory.getInstance() .createNewSettingsForPlayMode(timer.getPlayMode(), /* nameOfRaceToSort */ - selectedRaceIdentifier.getRaceName(), - /* nameOfRaceColumnToShow */null, /* nameOfRaceToShow */selectedRaceIdentifier.getRaceName()); + selectedRaceIdentifier.getRaceName(), + /* nameOfRaceColumnToShow */null, /* nameOfRaceToShow */selectedRaceIdentifier.getRaceName()); return new LeaderboardPanel(sailingService, asyncActionsExecutor, leaderBoardSettings, selectedRaceIdentifier, competitorSelectionModel, timer, leaderboardName, leaderboardGroupName, errorReporter, stringMessages, - userAgent, /* showRaceDetails */ true); - } + userAgent, /* showRaceDetails */true); + } private void addComponentToNavigationMenu(final ComponentViewer componentViewer, final Component component, boolean withCheckbox) { - final CheckBox checkBox= new CheckBox(component.getLocalizedShortName()); + final CheckBox checkBox = new CheckBox(component.getLocalizedShortName()); checkBox.getElement().getStyle().setFloat(Style.Float.LEFT); checkBox.setEnabled(withCheckbox); @@ -231,8 +244,8 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS }); componentsNavigationPanel.add(checkBox); - - if(component.hasSettings()) { + + if (component.hasSettings()) { Button settingsButton = new Button(""); settingsButton.addClickHandler(new ClickHandler() { @Override @@ -243,16 +256,16 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS settingsButton.addStyleName("raceBoardNavigation-settingsButton"); settingsButton.getElement().getStyle().setFloat(Style.Float.LEFT); settingsButton.setTitle(stringMessages.settingsForComponent(component.getLocalizedShortName())); - + componentsNavigationPanel.add(settingsButton); } } - + public void addComponentAsToogleButtonToNavigationMenu(final ComponentViewer componentViewer, final Component component) { - final ToggleButton toggleButton = new ToggleButton(component.getLocalizedShortName(), - "\u2713 " + component.getLocalizedShortName()); + final ToggleButton toggleButton = new ToggleButton(component.getLocalizedShortName(), "\u2713 " + + component.getLocalizedShortName()); toggleButton.getElement().getStyle().setFloat(Style.Float.LEFT); toggleButton.setDown(component.isVisible()); toggleButton.setTitle(stringMessages.showHideComponent(component.getLocalizedShortName())); @@ -276,19 +289,21 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS componentsNavigationPanel.add(toggleButton); } - + private void setComponentVisible(ComponentViewer componentViewer, Component component, boolean visible) { component.setVisible(visible); componentViewer.forceLayout(); } - + /** * Sets the collapsable panel for the leaderboard open or close, if in CASCADE view mode.
- * Displays or hides the leaderboard, if in ONESCREEN view mode.

+ * Displays or hides the leaderboard, if in ONESCREEN view mode.
+ *
* * The race board should be completely rendered before this method is called, or a few exceptions could be thrown. * - * @param visible true if the leaderboard shall be open/visible + * @param visible + * true if the leaderboard shall be open/visible */ public void setLeaderboardVisible(boolean visible) { switch (viewMode) { @@ -300,11 +315,13 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS /** * Sets the collapsable panel for the wind chart open or close, if in CASCADE view mode.
- * Displays or hides the wind chart, if in ONESCREEN view mode.

+ * Displays or hides the wind chart, if in ONESCREEN view mode.
+ *
* * The race board should be completely rendered before this method is called, or a few exceptions could be thrown. * - * @param visible true if the wind chart shall be open/visible + * @param visible + * true if the wind chart shall be open/visible */ public void setWindChartVisible(boolean visible) { switch (viewMode) { @@ -316,11 +333,13 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS /** * Sets the collapsable panel for the competitor chart open or close, if in CASCADE view mode.
- * Displays or hides the competitor chart, if in ONESCREEN view mode.

+ * Displays or hides the competitor chart, if in ONESCREEN view mode.
+ *
* * The race board should be completely rendered before this method is called, or a few exceptions could be thrown. * - * @param visible true if the competitor chart shall be open/visible + * @param visible + * true if the competitor chart shall be open/visible */ public void setCompetitorChartVisible(boolean visible) { switch (viewMode) { @@ -329,17 +348,17 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS break; } } - + public Widget getNavigationWidget() { - return componentsNavigationPanel; + return componentsNavigationPanel; } - + public Widget getSettingsWidget() { return settingsPanel; } public Widget getTimeWidget() { - return timePanel; + return timePanel; } protected SailingServiceAsync getSailingService() { @@ -357,15 +376,17 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS protected ErrorReporter getErrorReporter() { return errorReporter; } - + public int getScrollOffset() { return scrollOffset; } - + /** * Sets the offset, when scrolling with the menu entry anchors (in the top right corner).
* Only the absolute value of scrollOffset will be used. - * @param scrollOffset The new scrolling offset. Only the absolute value will be used. + * + * @param scrollOffset + * The new scrolling offset. Only the absolute value will be used. */ public void setScrollOffset(int scrollOffset) { this.scrollOffset = Math.abs(scrollOffset); @@ -387,4 +408,3 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS public void onRaceSelectionChange(List selectedRaces) { } } - diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java new file mode 100644 index 00000000000..4edf1fea939 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java @@ -0,0 +1,92 @@ +package com.sap.sailing.gwt.ui.raceboard; + +import java.util.Date; + +import com.google.gwt.dom.client.MediaElement; +import com.google.gwt.media.client.Video; +import com.google.gwt.user.client.ui.DialogBox; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.sap.sailing.gwt.ui.client.MediaPlayer; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack; + +public class VideoPopup extends DialogBox implements MediaPlayer { + + private static final int TOLERATED_LAG_IN_MILLISECONDS = 4000; + private Video videoControl; + private MediaTrack videoTrack; + + public VideoPopup() { + super(false, false); + VerticalPanel popUpPanelContents = new VerticalPanel(); + SimplePanel videoHolder = new SimplePanel(); + videoControl = Video.createIfSupported(); + if (videoControl != null) { + videoControl.setControls(true); + videoControl.setAutoplay(false); + videoHolder.add(videoControl); + + // HTML videoFrame = new + // HTML(""); + // + // SimplePanel videoFrameHolder = new SimplePanel(); + // videoFrameHolder.add(videoFrame); + + popUpPanelContents.add(videoHolder); + // popUpPanelContents.add(videoFrameHolder); + setWidget(popUpPanelContents); + } + } + + public void setMediaTrack(MediaTrack videoTrack) { + this.videoTrack = videoTrack; + setText(this.videoTrack.title); + if (videoControl != null) { + videoControl.setSrc(this.videoTrack.url); + } + } + + public void alignTime(Date raceTime) { + if (videoControl != null) { + switch (videoControl.getReadyState()) { + case MediaElement.HAVE_NOTHING: + pause(); + break; + default: + long videoStartTimeInMillis = videoTrack.startTime.getTime(); + long videoTimeInMillis = videoStartTimeInMillis + Math.round(videoControl.getCurrentTime() * 1000); + long raceTimeInMillis = raceTime.getTime(); + long videoLaggingBehindRaceInMillis = raceTimeInMillis - videoTimeInMillis; + if (Math.abs(videoLaggingBehindRaceInMillis) > TOLERATED_LAG_IN_MILLISECONDS) { + double videoTime = (raceTimeInMillis - videoStartTimeInMillis) / 1000; + if (videoTime < 0) { + pause(); + } else if (videoTime > videoControl.getDuration()) { + pause(); + } else { + videoControl.setCurrentTime(videoTime); + } + } + } + } + } + + @Override + public boolean isPaused() { + return (videoControl == null) || videoControl.isPaused(); + } + + @Override + public void pause() { + if (videoControl != null) { + videoControl.pause(); + } + } + + @Override + public void play() { + if (videoControl != null) { + videoControl.play(); + } + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoTrack.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoTrack.java new file mode 100644 index 00000000000..9aad135872d --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoTrack.java @@ -0,0 +1,37 @@ +package com.sap.sailing.gwt.ui.raceboard; + +import java.util.Date; + +import com.sap.sailing.domain.common.RaceIdentifier; + +public class VideoTrack { + + private final RaceIdentifier raceIdentifier; + private Date startTime; + // private final double lengthInSeconds; + private final String url; + + public RaceIdentifier getRaceIdentifier() { + return raceIdentifier; + } + + public Date getStartTime() { + return startTime; + } + + // public double getLengthInSeconds() { + // return lengthInSeconds; + // } + // + public VideoTrack(RaceIdentifier raceName, Date startTime, /* double lengthInSeconds, */String url) { + this.raceIdentifier = raceName; + this.startTime = startTime; + // this.lengthInSeconds = lengthInSeconds; + this.url = url; + } + + public String getUrl() { + return url; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java new file mode 100644 index 00000000000..2c4bde6acad --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java @@ -0,0 +1,79 @@ +package com.sap.sailing.gwt.ui.server; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.logging.Logger; + +import com.google.gwt.user.server.rpc.RemoteServiceServlet; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.persistence.media.DBMediaTrack; +import com.sap.sailing.domain.persistence.media.MediaDB; +import com.sap.sailing.domain.persistence.media.MediaDBFactory; +import com.sap.sailing.gwt.ui.client.MediaService; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaSubType; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaType; + +public class MediaServiceImpl extends RemoteServiceServlet implements MediaService { + + private static final Logger logger = Logger.getLogger(MediaServiceImpl.class.getName()); + + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + + private static final long serialVersionUID = -8917349579281305977L; + + public MediaServiceImpl() { + super(); + } + + @Override + public Collection getMediaTracksForRace(RegattaAndRaceIdentifier regattaAndRaceIdentifier) { + return getAllMediaTracks(); + } + + @Override + public List getAllMediaTracks() { + List allDBMediaTracks = mediaDB().loadAllMediaTracks(); + List result = new ArrayList(); + for (DBMediaTrack dbMediaTrack : allDBMediaTracks) { + MediaTrack mediaTrack = createMediaTrackFromDB(dbMediaTrack); + result.add(mediaTrack ); + } + return result; +// try { +// Date date = dateFormat.parse("2012-09-15 14:42:10 +0100"); +// return Arrays +// .asList(new MediaTrack[] { new MediaTrack( +// MediaType.VIDEO, +// "Day 2 Final - 49er Europeans 2012", +// "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv", +// date) }); +// } catch (ParseException e) { +// throw new RuntimeException(e); // just due to annoying date parse error potential +// } + } + + private MediaTrack createMediaTrackFromDB(DBMediaTrack dbMediaTrack) { + MediaType mediaType = MediaType.valueOf(dbMediaTrack.mimeType); + MediaSubType mediaSubType = MediaSubType.valueOf(dbMediaTrack.mimeSubType); + MediaTrack mediaTrack = new MediaTrack(dbMediaTrack.title, dbMediaTrack.url, dbMediaTrack.startTime, mediaType, mediaSubType); + return mediaTrack; + } + + @Override + public void addMediaTrack(MediaTrack mediaTrack) { + mediaDB().insertMediaTrack(mediaTrack.title, mediaTrack.url, mediaTrack.startTime, mediaTrack.type.name(), mediaTrack.subType.name()); + } + + private MediaDB mediaDB() { + return MediaDBFactory.INSTANCE.getDefaultMediaDB(); + } + + @Override + public void deleteMediaTrack(MediaTrack mediaTrack) { + mediaDB().deleteMediaTrack(mediaTrack.title); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java new file mode 100644 index 00000000000..c436f7c503b --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java @@ -0,0 +1,66 @@ +package com.sap.sailing.gwt.ui.shared.media; + +import java.util.Date; + +import com.google.gwt.user.client.rpc.IsSerializable; + +public class MediaTrack implements IsSerializable { + + public enum MimeFileType { + + mp4(MediaType.VIDEO, MediaSubType.mpeg), mp3(MediaType.AUDIO, MediaSubType.mpeg), ogv(MediaType.VIDEO, MediaSubType.ogg), qt(MediaType.VIDEO, MediaSubType.quicktime); + + public final MediaType mediaType; + public final MediaSubType mediaSubType; + + MimeFileType(MediaType mediaType, MediaSubType mediaSubType) { + this.mediaType = mediaType; + this.mediaSubType = mediaSubType; + } + + @Override + public String toString() { + return mediaType.name() + '/' + mediaSubType.toString(); + } + } + + public enum MediaType { + AUDIO, VIDEO; + } + + public enum MediaSubType { + ogg, mpeg, x_aiff, quicktime; + + public String toString() { + return name().replace('_', '-'); + }; + + } + + public class MediaSection implements IsSerializable { + public String title; + public long offsetInMillis; + public Date startTime; + public Date endTime; + } + + public String title; + public String url; + public Date startTime; + public MediaType type; + public MediaSubType subType; + + public MediaTrack() { + super(); + } + + public MediaTrack(String title, String url, Date startTime, MediaType type, MediaSubType subType) { + super(); + this.title = title; + this.url = url; + this.startTime = startTime; + this.type = type; + this.subType = subType; + } + +} diff --git a/java/com.sap.sailing.media.persistence.test/.classpath b/java/com.sap.sailing.media.persistence.test/.classpath new file mode 100644 index 00000000000..e8ea977a694 --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/java/com.sap.sailing.media.persistence.test/.gitignore b/java/com.sap.sailing.media.persistence.test/.gitignore new file mode 100644 index 00000000000..e660fd93d31 --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/.gitignore @@ -0,0 +1 @@ +bin/ diff --git a/java/com.sap.sailing.media.persistence.test/.project b/java/com.sap.sailing.media.persistence.test/.project new file mode 100644 index 00000000000..ab02faa2b7c --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/.project @@ -0,0 +1,28 @@ + + + com.sap.sailing.media.persistence.test + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/java/com.sap.sailing.media.persistence.test/.settings/org.eclipse.jdt.core.prefs b/java/com.sap.sailing.media.persistence.test/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..6efa4a4f1a4 --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +#Thu Jan 19 09:55:57 CET 2012 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/java/com.sap.sailing.media.persistence.test/.settings/org.eclipse.m2e.core.prefs b/java/com.sap.sailing.media.persistence.test/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 00000000000..1660d99c4cb --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,5 @@ +#Fri Jul 29 10:52:27 CEST 2011 +activeProfiles=snapshot.build +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/java/com.sap.sailing.media.persistence.test/.settings/org.eclipse.pde.core.prefs b/java/com.sap.sailing.media.persistence.test/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 00000000000..81b8c5dd408 --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,4 @@ +#Wed Jun 08 13:46:06 CEST 2011 +eclipse.preferences.version=1 +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..a3292d09da5 --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF @@ -0,0 +1,16 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Test +Bundle-SymbolicName: com.sap.sailing.media.persistence.test +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: SAP +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Require-Bundle: com.mongodb.driver;bundle-version="2.6.2", + com.sap.sailing.mongodb, + org.eclipse.osgi, + org.json.simple;bundle-version="1.1.0", + org.hamcrest.core;bundle-version="1.1.0", + org.junit4;bundle-version="4.8.2", + com.sap.sailing.domain.persistence;bundle-version="1.0.0", + com.sap.sailing.gwt.ui;bundle-version="1.0.0" + diff --git a/java/com.sap.sailing.media.persistence.test/build.properties b/java/com.sap.sailing.media.persistence.test/build.properties new file mode 100644 index 00000000000..34d2e4d2dad --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/java/com.sap.sailing.media.persistence.test/pom.xml b/java/com.sap.sailing.media.persistence.test/pom.xml new file mode 100644 index 00000000000..509f4d51152 --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/pom.xml @@ -0,0 +1,12 @@ + + + 4.0.0 + + root + com.sap.sailing + 1.0.0-SNAPSHOT + + com.sap.sailing.media.persistence.test + eclipse-test-plugin + diff --git a/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/AbstractMongoDBTest.java b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/AbstractMongoDBTest.java new file mode 100644 index 00000000000..9460b28fb6e --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/AbstractMongoDBTest.java @@ -0,0 +1,51 @@ +package com.sap.sailing.media.persistence.test; + +import static org.junit.Assert.assertNotNull; + +import java.net.UnknownHostException; + +import org.junit.Before; + +import com.mongodb.DB; +import com.mongodb.Mongo; +import com.mongodb.MongoException; +import com.sap.sailing.mongodb.MongoDBConfiguration; +import com.sap.sailing.mongodb.MongoDBService; + +public abstract class AbstractMongoDBTest { + protected Mongo mongo; + protected DB db; + private final MongoDBConfiguration dbConfiguration; + private MongoDBService service; + + public AbstractMongoDBTest() throws UnknownHostException, MongoException { + dbConfiguration = MongoDBConfiguration.getDefaultTestConfiguration(); + service = MongoDBService.INSTANCE; + service.setConfiguration(getDBConfiguration()); + mongo = newMongo(); + db = service.getDB(); + } + + protected MongoDBConfiguration getDBConfiguration() { + return dbConfiguration; + } + + protected Mongo newMongo() throws UnknownHostException, MongoException { + return new Mongo(System.getProperty("mongo.host", "127.0.0.1"), dbConfiguration.getPort()); + } + + @Before + public void dropTestDB() throws UnknownHostException, MongoException, InterruptedException { + assertNotNull(mongo); + dropAllCollections(db); + assertNotNull(db); + } + + private void dropAllCollections(DB theDB) throws InterruptedException { + db.dropDatabase(); + } + + protected MongoDBService getMongoService() { + return service; + } +} diff --git a/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/InitialLoad.java b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/InitialLoad.java new file mode 100644 index 00000000000..29fd48dfe98 --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/InitialLoad.java @@ -0,0 +1,13 @@ +package com.sap.sailing.media.persistence.test; + +public class InitialLoad { + + /** + * @param args + */ + public static void main(String[] args) { + // TODO Auto-generated method stub + + } + +} diff --git a/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestCRUDMedia.java b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestCRUDMedia.java new file mode 100644 index 00000000000..0b757ac1f48 --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestCRUDMedia.java @@ -0,0 +1,90 @@ +package com.sap.sailing.media.persistence.test; + +import java.net.UnknownHostException; +import java.util.Date; +import java.util.List; +import java.util.logging.Logger; + +import org.hamcrest.core.Is; +import org.junit.Test; + +import com.mongodb.MongoException; +import com.sap.sailing.domain.persistence.media.DBMediaTrack; +import com.sap.sailing.domain.persistence.media.MediaDB; +import com.sap.sailing.domain.persistence.media.MediaDBFactory; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaSubType; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaType; + +import static junit.framework.Assert.assertNotNull; + +import static org.junit.Assert.assertThat; + +public class TestCRUDMedia extends AbstractMongoDBTest { + + private static final Logger logger = Logger.getLogger(TestCRUDMedia.class.getName()); + + public TestCRUDMedia() throws UnknownHostException, MongoException { + super(); + } + + @Test + public void testCreateVideoTrack() { + final String videoTitle = "Test Video"; + final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; + MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); + Date date = new Date(); + String mediaType = MediaType.VIDEO.name(); + String mediaSubType = MediaSubType.ogg.name(); + mongoDB.insertMediaTrack(videoTitle, url, date, mediaType, mediaSubType); + DBMediaTrack videoTrack = mongoDB.loadMediaTrack(videoTitle); + assertNotNull(videoTrack); + assertThat(videoTrack.title, Is.is(videoTitle)); + } + + @Test(expected = RuntimeException.class) + public void testFailReadMissingVideoTrack() { + final String videoTitle = "Test Video"; + MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); + mongoDB.loadMediaTrack(videoTitle); + } + + @Test + public void testLoadAllVideoTracks() { + MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); + int testEntryCount = 10; + storeNumberOfTestMediaTracks(mongoDB, testEntryCount); + List allMediaTracks = mongoDB.loadAllMediaTracks(); + assertThat(allMediaTracks.size(), org.hamcrest.core.Is.is(testEntryCount)); + } + + private void storeNumberOfTestMediaTracks(MediaDB mongoDB, int count) { + final String videoTitleTemplate = "Test Video "; + final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; + Date date = new Date(); + String mediaType = MediaType.VIDEO.name(); + String mediaSubType = MediaSubType.ogg.name(); + + for (int i = 0; i < count; i++) { + mongoDB.insertMediaTrack(videoTitleTemplate + i, url, date, mediaType, mediaSubType); + } + } + + @Test(expected=RuntimeException.class) + public void testDeleteVideoTrack() { + //insert test object + final String videoTitle = "Test Video"; + final String url = "test"; + MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); + Date date = new Date(); + String mediaType = MediaType.VIDEO.name(); + String mediaSubType = MediaSubType.ogg.name(); + mongoDB.insertMediaTrack(videoTitle, url, date, mediaType, mediaSubType); + + //delete + mongoDB.deleteMediaTrack(videoTitle); + + //assert not exists --> RuntimeException + mongoDB.loadMediaTrack(videoTitle); + } + +} diff --git a/java/target/configuration/jetty/etc/realm.properties b/java/target/configuration/jetty/etc/realm.properties index 6d0796b507f..77409ea21df 100644 --- a/java/target/configuration/jetty/etc/realm.properties +++ b/java/target/configuration/jetty/etc/realm.properties @@ -1,25 +1,23 @@ -# -# This file defines users passwords and roles for a HashUserRealm -# -# The format is -# : [, ...] -# -# Passwords may be clear text, obfuscated or checksummed. The class -# org.eclipse.util.Password should be used to generate obfuscated -# passwords or password checksums -# -# If DIGEST Authentication is used, the password must be in a recoverable -# format, either plain text or OBF:. -# -administrator: sailing,spectator,moderator,administrator -axel: axel,spectator,moderator,administrator -frank: frank,spectator,moderator,administrator -lennart: lennart,spectator,moderator,administrator -marcus: marcus,spectator,moderator,administrator -milan: milan,spectator,moderator,administrator -nils: nils,spectator,moderator,administrator -stefan: stefan,spectator,moderator,administrator -riccardo: riccardo,spectator,moderator,administrator -christopher: christopher,spectator,moderator,administrator -spectator: spectator,spectator -moderator: moderator,moderator +# +# This file defines users passwords and roles for a HashUserRealm +# +# The format is +# : [, ...] +# +# Passwords may be clear text, obfuscated or checksummed. The class +# org.eclipse.util.Password should be used to generate obfuscated +# passwords or password checksums +# +# If DIGEST Authentication is used, the password must be in a recoverable +# format, either plain text or OBF:. +# +axel: axel,spectator,moderator,administrator +frank: frank,spectator,moderator,administrator +jens: jens,spectator,moderator,administrator +lennart: lennart,spectator,moderator,administrator +marcus: marcus,spectator,moderator,administrator +milan: milan,spectator,moderator,administrator +nils: nils,spectator,moderator,administrator +stefan: stefan,spectator,moderator,administrator +riccardo: riccardo,spectator,moderator,administrator +christopher: christopher,spectator,moderator,administrator From c5c1593dc03b04da3db7cd171cca827637e415a0 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Thu, 15 Nov 2012 19:00:13 +0100 Subject: [PATCH 02/31] work commit --- .../gwt/ui/adminconsole/MediaPanel.java | 55 ++++++++++++++++-- .../gwt/ui/adminconsole/MediaTrackDialog.java | 31 +++++++++- .../gwt/ui/adminconsole/TimeFormatUtil.java | 38 ++++++++++++ .../sailing/gwt/ui/client/MediaService.java | 6 +- .../gwt/ui/client/MediaServiceAsync.java | 6 +- .../sailing/gwt/ui/client/StringMessages.java | 2 + .../gwt/ui/client/StringMessages.properties | 3 +- .../ui/client/StringMessages_de.properties | 3 +- .../gwt/ui/server/MediaServiceImpl.java | 58 +++++++++++++++---- .../gwt/ui/shared/media/MediaTrack.java | 15 ++++- 10 files changed, 192 insertions(+), 25 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimeFormatUtil.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java index f3a5e0b1a4a..17dcb041acf 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java @@ -1,7 +1,7 @@ package com.sap.sailing.gwt.ui.adminconsole; +import java.util.Collection; import java.util.Comparator; -import java.util.List; import com.google.gwt.cell.client.ActionCell; import com.google.gwt.cell.client.EditTextCell; @@ -38,7 +38,7 @@ import com.sap.sailing.gwt.ui.shared.media.MediaTrack; */ public class MediaPanel extends FlowPanel { - private static final DateTimeFormat TIME_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.HOUR24_MINUTE_SECOND); +// private static final DateTimeFormat TIME_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.HOUR24_MINUTE_SECOND); private static final DateTimeFormat DATETIME_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.DATE_TIME_MEDIUM); // private Calendar calender = new GregorianCalendar(); @@ -80,7 +80,7 @@ public class MediaPanel extends FlowPanel { protected void loadMediaTracks() { mediaTrackListDataProvider.getList().clear(); - mediaService.getAllMediaTracks(new AsyncCallback>() { + mediaService.getAllMediaTracks(new AsyncCallback>() { @Override public void onFailure(Throwable t) { @@ -88,7 +88,7 @@ public class MediaPanel extends FlowPanel { } @Override - public void onSuccess(List allMediaTracks) { + public void onSuccess(Collection allMediaTracks) { mediaTrackListDataProvider.getList().addAll(allMediaTracks); mediaTrackListDataProvider.refresh(); } @@ -255,12 +255,57 @@ public class MediaPanel extends FlowPanel { public void update(int index, MediaTrack mediaTrack, String newStartTime) { // Called when the user changes the value. mediaTrack.startTime = DATETIME_FORMAT.parse(newStartTime); - mediaTrackListDataProvider.refresh(); + mediaService.updateStartTime(mediaTrack, new AsyncCallback() { + + @Override + public void onFailure(Throwable t) { + errorReporter.reportError(t.toString()); + } + + @Override + public void onSuccess(Void allMediaTracks) { + mediaTrackListDataProvider.refresh(); + } + }); } }); mediaTracksTable.addColumn(startTimeColumn, stringMessages.startTime()); mediaTracksTable.setColumnWidth(startTimeColumn, 100, Unit.PCT); + // duration + Column durationColumn = new Column(new EditTextCell()) { + @Override + public String getValue(MediaTrack mediaTrack) { + return TimeFormatUtil.milliSecondsToHrsMinSec(mediaTrack.durationInMillis); + } + }; + durationColumn.setSortable(true); + sortHandler.setComparator(durationColumn, new Comparator() { + public int compare(MediaTrack mediaTrack1, MediaTrack mediaTrack2) { + return Integer.valueOf(mediaTrack1.durationInMillis).compareTo(Integer.valueOf(mediaTrack2.durationInMillis)); + } + }); + durationColumn.setFieldUpdater(new FieldUpdater() { + public void update(int index, MediaTrack mediaTrack, String newDuration) { + // Called when the user changes the value. + mediaTrack.durationInMillis = TimeFormatUtil.hrsMinSecToMilliSeconds(newDuration); + mediaService.updateDuration(mediaTrack, new AsyncCallback() { + + @Override + public void onFailure(Throwable t) { + errorReporter.reportError(t.toString()); + } + + @Override + public void onSuccess(Void allMediaTracks) { + mediaTrackListDataProvider.refresh(); + } + }); + } + }); + mediaTracksTable.addColumn(durationColumn, stringMessages.duration()); + mediaTracksTable.setColumnWidth(durationColumn, 100, Unit.PCT); + // delete action Column deleteActionColumn = new IdentityColumn(new ActionCell("X", new ActionCell.Delegate() { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java index 8e1830ebfaa..d8d46307e80 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java @@ -2,8 +2,10 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.Date; +import com.google.gwt.dom.client.VideoElement; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; +import com.google.gwt.media.client.Video; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Label; @@ -64,10 +66,37 @@ public class MediaTrackDialog extends DataEntryDialog { String title = nameBox.getValue(); String url = urlBox.getValue(); Date startTime = startTimeBox.getValue(); - MediaTrack result = new MediaTrack(title, url, startTime, mediaType, mediaSubType); + int durationInMillis = getMediaDuration(url, mediaType, mediaSubType); + MediaTrack result = new MediaTrack(title, url, startTime, durationInMillis, mediaType, mediaSubType); return result; } + private int getMediaDuration(String url, MediaType mediaType, MediaSubType mediaSubType) { + if (mediaType != null) { + switch (mediaType) { + case VIDEO: + Video videoControl = Video.createIfSupported(); + if (videoControl != null) { + videoControl.setSrc(url); + VideoElement videoElement = videoControl.getVideoElement(); + double result = videoElement.getDuration(); + videoElement.play(); + + long timeout = System.currentTimeMillis() + 5 * 1000; + while (System.currentTimeMillis() < timeout) { + if (videoElement.getReadyState() > 0) { + return (int) Math.round(videoElement.getDuration() * 1000); + } + } + } + default: + return 0; + } + } else { + return 0; + } + } + @Override protected Widget getAdditionalWidget() { VerticalPanel mainPanel = new VerticalPanel(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimeFormatUtil.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimeFormatUtil.java new file mode 100644 index 00000000000..a814ae873f5 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimeFormatUtil.java @@ -0,0 +1,38 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.i18n.client.NumberFormat; +import com.sap.sailing.gwt.ui.client.NumberFormatterFactory; + +public class TimeFormatUtil { + + private static NumberFormat zeroPaddingNumberFormat_Min = NumberFormatterFactory.getDecimalFormat(2, 0); + private static NumberFormat zeroPaddingNumberFormat_Sec = NumberFormatterFactory.getDecimalFormat(2, 3); + + public static int hrsMinSecToMilliSeconds(String hrsMinSec) { + String[] segements = hrsMinSec.split(":"); + int milliseconds = 0; + switch (segements.length) { + case 3: + String hours = segements[0].trim(); + milliseconds = Integer.valueOf(hours) * 60 * 60 * 1000; + case 2: + String minutes = segements[segements.length - 2].trim(); + milliseconds = milliseconds + Integer.valueOf(minutes) * 60 * 1000; + case 1: + String seconds = segements[segements.length - 1].trim(); + milliseconds = milliseconds + Math.round(Float.valueOf(seconds) * 1000); + } + return milliseconds; + } + + public static String milliSecondsToHrsMinSec(int milliseconds) { + int hours = (milliseconds / (60 * 60 * 1000)); + int rest = (milliseconds % (60 * 60 * 1000)); + int minutes = (rest / (60 * 1000)); + rest = (rest % (60 * 1000)); + double seconds = (double) rest / 1000; + String result = String.valueOf(hours) + ':' + zeroPaddingNumberFormat_Min.format(minutes) + ':' + zeroPaddingNumberFormat_Sec.format(seconds); + return result; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java index 43c4231ecec..4e45a335338 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java @@ -13,10 +13,14 @@ public interface MediaService extends RemoteService { Collection getMediaTracksForRace(RegattaAndRaceIdentifier regattaAndRaceIdentifier); - List getAllMediaTracks(); + Collection getAllMediaTracks(); void addMediaTrack(MediaTrack mediaTrack); void deleteMediaTrack(MediaTrack mediaTrack); + void updateStartTime(MediaTrack mediaTrack); + + void updateDuration(MediaTrack mediaTrack); + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java index 440dfe6a19c..29c4930058c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java @@ -12,10 +12,14 @@ public interface MediaServiceAsync { void getMediaTracksForRace(RegattaAndRaceIdentifier regattaAndRaceIdentifier, AsyncCallback> callback); - void getAllMediaTracks(AsyncCallback> asyncCallback); + void getAllMediaTracks(AsyncCallback> asyncCallback); void addMediaTrack(MediaTrack mediaTrack, AsyncCallback asyncCallback); void deleteMediaTrack(MediaTrack mediaTrack, AsyncCallback asyncCallback); + void updateStartTime(MediaTrack mediaTrack, AsyncCallback asyncCallback); + + void updateDuration(MediaTrack mediaTrack, AsyncCallback asyncCallback); + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 24712b7e58a..5a845a6fcc2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.ui.client; import com.google.gwt.i18n.client.Messages; +import com.google.gwt.user.cellview.client.Header; public interface StringMessages extends Messages { String helloWorld(); @@ -404,4 +405,5 @@ public interface StringMessages extends Messages { String mimeType(); String title(); String reallyRemoveMediaTrack(String mediaTrackTitle); + String duration(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 27bc5f19aeb..ddfd0c801b6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -403,4 +403,5 @@ mediaPanel=Audio & Video url=URL mimeType=MIME Type title=Title -reallyRemoveMediaTrack=Do you really want to remove media track ''{0}''? \ No newline at end of file +reallyRemoveMediaTrack=Do you really want to remove media track ''{0}''? +duration=Duration (h:mm:ss.ms) \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 0cbc07b58b4..1d44f386535 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -404,4 +404,5 @@ mediaPanel=Audio & Video url=URL mimeType=MIME Type title=Titel -reallyRemoveMediaTrack=Möchten Sie ''{0}'' wirklich entfernen? \ No newline at end of file +reallyRemoveMediaTrack=Möchten Sie ''{0}'' wirklich entfernen? +duration=Dauer (h:mm:ss.ms) \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java index 2c4bde6acad..dafc60deae6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java @@ -4,44 +4,59 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.Date; import java.util.List; -import java.util.logging.Logger; + +import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.persistence.media.DBMediaTrack; import com.sap.sailing.domain.persistence.media.MediaDB; import com.sap.sailing.domain.persistence.media.MediaDBFactory; +import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.gwt.ui.client.MediaService; import com.sap.sailing.gwt.ui.shared.media.MediaTrack; import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaSubType; import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaType; +import com.sap.sailing.server.RacingEventService; public class MediaServiceImpl extends RemoteServiceServlet implements MediaService { - private static final Logger logger = Logger.getLogger(MediaServiceImpl.class.getName()); +// private static final Logger logger = Logger.getLogger(MediaServiceImpl.class.getName()); DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + private ServiceTracker racingEventServiceTracker; + private static final long serialVersionUID = -8917349579281305977L; public MediaServiceImpl() { super(); + BundleContext context = Activator.getDefault(); + racingEventServiceTracker = new ServiceTracker( + context, RacingEventService.class.getName(), null); + racingEventServiceTracker.open(); } @Override public Collection getMediaTracksForRace(RegattaAndRaceIdentifier regattaAndRaceIdentifier) { - return getAllMediaTracks(); + TrackedRace trackedRace = racingEventServiceTracker.getService().getExistingTrackedRace(regattaAndRaceIdentifier); + if (trackedRace != null) { + Date raceStart = trackedRace.getStartOfTracking().asDate(); + Date raceEnd = trackedRace.getStartOfTracking().asDate(); + return createMediaTracksFromDB(mediaDB().queryOverlappingMediaTracks(raceStart , raceEnd)); + } else { + return Collections.emptyList(); + } } @Override - public List getAllMediaTracks() { + public Collection getAllMediaTracks() { List allDBMediaTracks = mediaDB().loadAllMediaTracks(); - List result = new ArrayList(); - for (DBMediaTrack dbMediaTrack : allDBMediaTracks) { - MediaTrack mediaTrack = createMediaTrackFromDB(dbMediaTrack); - result.add(mediaTrack ); - } + Collection result = createMediaTracksFromDB(allDBMediaTracks); return result; // try { // Date date = dateFormat.parse("2012-09-15 14:42:10 +0100"); @@ -56,16 +71,25 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi // } } + private Collection createMediaTracksFromDB(Collection allDBMediaTracks) { + Collection result = new ArrayList(); + for (DBMediaTrack dbMediaTrack : allDBMediaTracks) { + MediaTrack mediaTrack = createMediaTrackFromDB(dbMediaTrack); + result.add(mediaTrack ); + } + return result; + } + private MediaTrack createMediaTrackFromDB(DBMediaTrack dbMediaTrack) { MediaType mediaType = MediaType.valueOf(dbMediaTrack.mimeType); MediaSubType mediaSubType = MediaSubType.valueOf(dbMediaTrack.mimeSubType); - MediaTrack mediaTrack = new MediaTrack(dbMediaTrack.title, dbMediaTrack.url, dbMediaTrack.startTime, mediaType, mediaSubType); + MediaTrack mediaTrack = new MediaTrack(dbMediaTrack.dbId, dbMediaTrack.title, dbMediaTrack.url, dbMediaTrack.startTime, dbMediaTrack.durationInMillis, mediaType, mediaSubType); return mediaTrack; } @Override public void addMediaTrack(MediaTrack mediaTrack) { - mediaDB().insertMediaTrack(mediaTrack.title, mediaTrack.url, mediaTrack.startTime, mediaTrack.type.name(), mediaTrack.subType.name()); + mediaDB().insertMediaTrack(mediaTrack.title, mediaTrack.url, mediaTrack.startTime, mediaTrack.durationInMillis, mediaTrack.type.name(), mediaTrack.subType.name()); } private MediaDB mediaDB() { @@ -74,6 +98,16 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi @Override public void deleteMediaTrack(MediaTrack mediaTrack) { - mediaDB().deleteMediaTrack(mediaTrack.title); + mediaDB().deleteMediaTrack(mediaTrack.dbId); + } + + @Override + public void updateStartTime(MediaTrack mediaTrack) { + mediaDB().updateStartTime(mediaTrack.dbId, mediaTrack.startTime); + } + + @Override + public void updateDuration(MediaTrack mediaTrack) { + mediaDB().updateDuration(mediaTrack.dbId, mediaTrack.durationInMillis); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java index c436f7c503b..b34842c62d6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java @@ -44,9 +44,11 @@ public class MediaTrack implements IsSerializable { public Date endTime; } + public String dbId; public String title; public String url; public Date startTime; + public int durationInMillis; public MediaType type; public MediaSubType subType; @@ -54,13 +56,20 @@ public class MediaTrack implements IsSerializable { super(); } - public MediaTrack(String title, String url, Date startTime, MediaType type, MediaSubType subType) { + public MediaTrack(String title, String url, Date startTime, int durationInMillis, MediaType mediaType, MediaSubType mediaSubType) { + this(null, title, url, startTime, durationInMillis, mediaType, mediaSubType); + } + + public MediaTrack(String dbId, String title, String url, Date startTime, int durationInMillis, MediaType mediaType, + MediaSubType mediaSubType) { super(); + this.dbId = dbId; this.title = title; this.url = url; this.startTime = startTime; - this.type = type; - this.subType = subType; + this.durationInMillis = durationInMillis; + this.type = mediaType; + this.subType = mediaSubType; } } From 317b4e8a780f9c4448c83a963d375133f809948f Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Thu, 15 Nov 2012 19:00:13 +0100 Subject: [PATCH 03/31] work commit --- .../persistence/media/DBMediaTrack.java | 6 +- .../domain/persistence/media/MediaDB.java | 13 +- .../persistence/media/impl/DbNames.java | 2 +- .../persistence/media/impl/MediaDBImpl.java | 84 ++++++- .../media/impl/MediaObjectFactoryImpl.java | 8 +- .../gwt/ui/adminconsole/MediaPanel.java | 55 ++++- .../gwt/ui/adminconsole/MediaTrackDialog.java | 31 ++- .../gwt/ui/adminconsole/TimeFormatUtil.java | 38 +++ .../sailing/gwt/ui/client/MediaService.java | 6 +- .../gwt/ui/client/MediaServiceAsync.java | 6 +- .../sailing/gwt/ui/client/StringMessages.java | 2 + .../gwt/ui/client/StringMessages.properties | 3 +- .../ui/client/StringMessages_de.properties | 3 +- .../gwt/ui/server/MediaServiceImpl.java | 58 ++++- .../gwt/ui/shared/media/MediaTrack.java | 15 +- .../META-INF/MANIFEST.MF | 3 +- .../media/persistence/test/TestCRUDMedia.java | 90 ------- .../media/persistence/test/TestMediaDB.java | 232 ++++++++++++++++++ 18 files changed, 516 insertions(+), 139 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimeFormatUtil.java delete mode 100644 java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestCRUDMedia.java create mode 100644 java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestMediaDB.java diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java index 50ee4d74816..9070c8e750c 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java @@ -4,17 +4,21 @@ import java.util.Date; public class DBMediaTrack { + public final String dbId; public final String title; public final String url; public final Date startTime; + public final int durationInMillis; public final String mimeType; public final String mimeSubType; - public DBMediaTrack(String title, String url, Date startTime, String mimeType, String mimeSubType) { + public DBMediaTrack(String dbId, String title, String url, Date startTime, int duration, String mimeType, String mimeSubType) { super(); + this.dbId = dbId; this.title = title; this.url = url; this.startTime = startTime; + this.durationInMillis = duration; this.mimeType = mimeType; this.mimeSubType = mimeSubType; } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java index 37fbd9d6eed..333d2e91420 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java @@ -1,10 +1,9 @@ package com.sap.sailing.domain.persistence.media; +import java.util.Collection; import java.util.Date; import java.util.List; -import com.mongodb.DBObject; - /** * Offers CRUD methods for mongo representation of media track objects. * @@ -13,12 +12,18 @@ import com.mongodb.DBObject; */ public interface MediaDB { - void insertMediaTrack(String videoTitle, String url, Date startTime, String mediaType, String mediaSubType); + String insertMediaTrack(String title, String url, Date startTime, int durationInMillis, String mediaType, String mediaSubType); - DBMediaTrack loadMediaTrack(String videoTitle); + DBMediaTrack loadMediaTrack(String dbId); List loadAllMediaTracks(); void deleteMediaTrack(String title); + void updateStartTime(String title, Date startTime); + + void updateDuration(String dbId, int durationInMillis); + + Collection queryOverlappingMediaTracks(Date rangeStart, Date rangeEnd); + } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/DbNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/DbNames.java index 42616bbb1dc..60b59e84fa4 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/DbNames.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/DbNames.java @@ -3,7 +3,7 @@ package com.sap.sailing.domain.persistence.media.impl; public class DbNames { public enum Fields { - MEDIA_TITLE, MEDIA_URL, STARTTIME, MIME_TYPE, MIME_SUBTYPE; + _id, MEDIA_TITLE, MEDIA_URL, STARTTIME, DURATION_IN_MILLIS, MIME_TYPE, MIME_SUBTYPE; } public enum Collections { diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java index df23c52d238..06840cfd8ee 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java @@ -1,23 +1,30 @@ package com.sap.sailing.domain.persistence.media.impl; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.List; -import java.util.logging.Logger; + +import org.bson.types.ObjectId; import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; -import com.mongodb.WriteResult; -import com.sap.sailing.domain.persistence.impl.FieldNames; +import com.mongodb.QueryBuilder; import com.sap.sailing.domain.persistence.media.DBMediaTrack; import com.sap.sailing.domain.persistence.media.MediaDB; +/** + * MongoDB Java API examples: http://blog.rasc.ch/?p=1096 + * + * @author D047974 + * + */ public class MediaDBImpl implements MediaDB { - private static Logger logger = Logger.getLogger(MediaDBImpl.class.getName()); + // private static Logger logger = Logger.getLogger(MediaDBImpl.class.getName()); private final DB database; public MediaDBImpl(DB database) { @@ -26,15 +33,17 @@ public class MediaDBImpl implements MediaDB { } @Override - public void insertMediaTrack(String videoTitle, String url, Date startTime, String mediaType, String mediaSubType) { + public String insertMediaTrack(String videoTitle, String url, Date startTime, int durationInMillis, String mediaType, String mediaSubType) { BasicDBObject dbVideo = new BasicDBObject(); dbVideo.put(DbNames.Fields.MEDIA_TITLE.name(), videoTitle); dbVideo.put(DbNames.Fields.MEDIA_URL.name(), url); dbVideo.put(DbNames.Fields.STARTTIME.name(), startTime); + dbVideo.put(DbNames.Fields.DURATION_IN_MILLIS.name(), durationInMillis); dbVideo.put(DbNames.Fields.MIME_TYPE.name(), mediaType); dbVideo.put(DbNames.Fields.MIME_SUBTYPE.name(), mediaSubType); DBCollection dbVideos = getVideoCollection(); dbVideos.insert(dbVideo); + return ((ObjectId) dbVideo.get(DbNames.Fields._id.name())).toString(); } private DBCollection getVideoCollection() { @@ -49,28 +58,31 @@ public class MediaDBImpl implements MediaDB { } @Override - public DBMediaTrack loadMediaTrack(String videoTitle) { - DBCursor cursor = getVideoCollection().find(new BasicDBObject(DbNames.Fields.MEDIA_TITLE.name(), videoTitle)); + public DBMediaTrack loadMediaTrack(String dbId) { + ObjectId objectId = new ObjectId(dbId); + DBCursor cursor = getVideoCollection().find(new BasicDBObject(DbNames.Fields._id.name(), objectId)); switch (cursor.count()) { case 0: - throw new RuntimeException("Video not found with title: " + videoTitle); + throw new RuntimeException("Video not found with id: " + dbId); case 1: DBObject dbObject = cursor.next(); DBMediaTrack dbMediaTrack = createMediaObjectFromDB(dbObject); return dbMediaTrack; default: - throw new RuntimeException("Ambiguous videos not found with title: " + videoTitle); + throw new RuntimeException("Ambiguous videos not found with id: " + dbId); } } private DBMediaTrack createMediaObjectFromDB(DBObject dbObject) { + String dbId = ((ObjectId) dbObject.get(DbNames.Fields._id.name())).toString(); String title = (String) dbObject.get(DbNames.Fields.MEDIA_TITLE.name()); String url = (String) dbObject.get(DbNames.Fields.MEDIA_URL.name()); Date startTime = (Date) dbObject.get(DbNames.Fields.STARTTIME.name()); + Integer durationInMillis = (Integer) dbObject.get(DbNames.Fields.DURATION_IN_MILLIS.name()); String mimeType = (String) dbObject.get(DbNames.Fields.MIME_TYPE.name()); String mimeSubType = (String) dbObject.get(DbNames.Fields.MIME_SUBTYPE.name()); - DBMediaTrack dbMediaTrack = new DBMediaTrack(title, url, startTime, mimeType, mimeSubType); + DBMediaTrack dbMediaTrack = new DBMediaTrack(dbId, title, url, startTime, durationInMillis == null ? 0 : durationInMillis, mimeType, mimeSubType); return dbMediaTrack; } @@ -85,9 +97,57 @@ public class MediaDBImpl implements MediaDB { } @Override - public void deleteMediaTrack(String title) { + public void deleteMediaTrack(String dbId) { BasicDBObject dbObject = new BasicDBObject(); - dbObject.put(DbNames.Fields.MEDIA_TITLE.name(), title); + dbObject.put(DbNames.Fields._id.name(), new ObjectId(dbId)); getVideoCollection().remove(dbObject); } + + @Override + public void updateStartTime(String dbId, Date startTime) { + BasicDBObject updateQuery = new BasicDBObject(); + updateQuery.append(DbNames.Fields._id.name(), new ObjectId(dbId)); + + BasicDBObject update = new BasicDBObject(); + update.append("$set", new BasicDBObject(DbNames.Fields.STARTTIME.name(), startTime)); + + getVideoCollection().update(updateQuery, update); + } + + @Override + public void updateDuration(String dbId, int durationInMillis) { + BasicDBObject updateQuery = new BasicDBObject(); + updateQuery.append(DbNames.Fields._id.name(), new ObjectId(dbId)); + + BasicDBObject update = new BasicDBObject(); + update.append("$set", new BasicDBObject(DbNames.Fields.DURATION_IN_MILLIS.name(), durationInMillis)); + + getVideoCollection().update(updateQuery, update); + } + + @Override + public Collection queryOverlappingMediaTracks(Date rangeStart, Date rangeEnd) { + BasicDBObject startTimeCondition = new BasicDBObject(); + startTimeCondition.put(DbNames.Fields.STARTTIME.name(), new BasicDBObject("$gt", rangeStart)); + startTimeCondition.put(DbNames.Fields.STARTTIME.name(), new BasicDBObject("$lt", rangeEnd)); + BasicDBObject endTimeCondition = new BasicDBObject(); + + // Should actually be "AND greater than rangeStart - duration". + // However, using values calculated on server side turns out to be a nightmare with mongodb. + // Instead do the remaining filtering on client side... + endTimeCondition.put(DbNames.Fields.STARTTIME.name(), new BasicDBObject("$lt", rangeStart)); + + DBObject query = QueryBuilder.start().or(startTimeCondition, endTimeCondition).get(); + DBCursor cursor = getVideoCollection().find(query); + + List result = new ArrayList<>(cursor.count()); + while (cursor.hasNext()) { + DBMediaTrack resultCandidate = createMediaObjectFromDB(cursor.next()); + if (resultCandidate.durationInMillis > rangeStart.getTime() - resultCandidate.startTime.getTime()) { + result.add(resultCandidate); + } + } + return result; + } + } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaObjectFactoryImpl.java index 2f6a07b9339..3eeb7c323e0 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaObjectFactoryImpl.java @@ -1,19 +1,17 @@ package com.sap.sailing.domain.persistence.media.impl; -import java.util.logging.Logger; - import com.mongodb.DB; import com.sap.sailing.domain.persistence.media.MediaObjectFactory; public class MediaObjectFactoryImpl implements MediaObjectFactory { - private static final Logger logger = Logger.getLogger(MediaObjectFactoryImpl.class.getName()); +// private static final Logger logger = Logger.getLogger(MediaObjectFactoryImpl.class.getName()); - private final DB database; +// private final DB database; public MediaObjectFactoryImpl(DB db) { super(); - this.database = db; +// this.database = db; } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java index f3a5e0b1a4a..17dcb041acf 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java @@ -1,7 +1,7 @@ package com.sap.sailing.gwt.ui.adminconsole; +import java.util.Collection; import java.util.Comparator; -import java.util.List; import com.google.gwt.cell.client.ActionCell; import com.google.gwt.cell.client.EditTextCell; @@ -38,7 +38,7 @@ import com.sap.sailing.gwt.ui.shared.media.MediaTrack; */ public class MediaPanel extends FlowPanel { - private static final DateTimeFormat TIME_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.HOUR24_MINUTE_SECOND); +// private static final DateTimeFormat TIME_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.HOUR24_MINUTE_SECOND); private static final DateTimeFormat DATETIME_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.DATE_TIME_MEDIUM); // private Calendar calender = new GregorianCalendar(); @@ -80,7 +80,7 @@ public class MediaPanel extends FlowPanel { protected void loadMediaTracks() { mediaTrackListDataProvider.getList().clear(); - mediaService.getAllMediaTracks(new AsyncCallback>() { + mediaService.getAllMediaTracks(new AsyncCallback>() { @Override public void onFailure(Throwable t) { @@ -88,7 +88,7 @@ public class MediaPanel extends FlowPanel { } @Override - public void onSuccess(List allMediaTracks) { + public void onSuccess(Collection allMediaTracks) { mediaTrackListDataProvider.getList().addAll(allMediaTracks); mediaTrackListDataProvider.refresh(); } @@ -255,12 +255,57 @@ public class MediaPanel extends FlowPanel { public void update(int index, MediaTrack mediaTrack, String newStartTime) { // Called when the user changes the value. mediaTrack.startTime = DATETIME_FORMAT.parse(newStartTime); - mediaTrackListDataProvider.refresh(); + mediaService.updateStartTime(mediaTrack, new AsyncCallback() { + + @Override + public void onFailure(Throwable t) { + errorReporter.reportError(t.toString()); + } + + @Override + public void onSuccess(Void allMediaTracks) { + mediaTrackListDataProvider.refresh(); + } + }); } }); mediaTracksTable.addColumn(startTimeColumn, stringMessages.startTime()); mediaTracksTable.setColumnWidth(startTimeColumn, 100, Unit.PCT); + // duration + Column durationColumn = new Column(new EditTextCell()) { + @Override + public String getValue(MediaTrack mediaTrack) { + return TimeFormatUtil.milliSecondsToHrsMinSec(mediaTrack.durationInMillis); + } + }; + durationColumn.setSortable(true); + sortHandler.setComparator(durationColumn, new Comparator() { + public int compare(MediaTrack mediaTrack1, MediaTrack mediaTrack2) { + return Integer.valueOf(mediaTrack1.durationInMillis).compareTo(Integer.valueOf(mediaTrack2.durationInMillis)); + } + }); + durationColumn.setFieldUpdater(new FieldUpdater() { + public void update(int index, MediaTrack mediaTrack, String newDuration) { + // Called when the user changes the value. + mediaTrack.durationInMillis = TimeFormatUtil.hrsMinSecToMilliSeconds(newDuration); + mediaService.updateDuration(mediaTrack, new AsyncCallback() { + + @Override + public void onFailure(Throwable t) { + errorReporter.reportError(t.toString()); + } + + @Override + public void onSuccess(Void allMediaTracks) { + mediaTrackListDataProvider.refresh(); + } + }); + } + }); + mediaTracksTable.addColumn(durationColumn, stringMessages.duration()); + mediaTracksTable.setColumnWidth(durationColumn, 100, Unit.PCT); + // delete action Column deleteActionColumn = new IdentityColumn(new ActionCell("X", new ActionCell.Delegate() { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java index 8e1830ebfaa..d8d46307e80 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java @@ -2,8 +2,10 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.Date; +import com.google.gwt.dom.client.VideoElement; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; +import com.google.gwt.media.client.Video; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Label; @@ -64,10 +66,37 @@ public class MediaTrackDialog extends DataEntryDialog { String title = nameBox.getValue(); String url = urlBox.getValue(); Date startTime = startTimeBox.getValue(); - MediaTrack result = new MediaTrack(title, url, startTime, mediaType, mediaSubType); + int durationInMillis = getMediaDuration(url, mediaType, mediaSubType); + MediaTrack result = new MediaTrack(title, url, startTime, durationInMillis, mediaType, mediaSubType); return result; } + private int getMediaDuration(String url, MediaType mediaType, MediaSubType mediaSubType) { + if (mediaType != null) { + switch (mediaType) { + case VIDEO: + Video videoControl = Video.createIfSupported(); + if (videoControl != null) { + videoControl.setSrc(url); + VideoElement videoElement = videoControl.getVideoElement(); + double result = videoElement.getDuration(); + videoElement.play(); + + long timeout = System.currentTimeMillis() + 5 * 1000; + while (System.currentTimeMillis() < timeout) { + if (videoElement.getReadyState() > 0) { + return (int) Math.round(videoElement.getDuration() * 1000); + } + } + } + default: + return 0; + } + } else { + return 0; + } + } + @Override protected Widget getAdditionalWidget() { VerticalPanel mainPanel = new VerticalPanel(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimeFormatUtil.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimeFormatUtil.java new file mode 100644 index 00000000000..a814ae873f5 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimeFormatUtil.java @@ -0,0 +1,38 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.i18n.client.NumberFormat; +import com.sap.sailing.gwt.ui.client.NumberFormatterFactory; + +public class TimeFormatUtil { + + private static NumberFormat zeroPaddingNumberFormat_Min = NumberFormatterFactory.getDecimalFormat(2, 0); + private static NumberFormat zeroPaddingNumberFormat_Sec = NumberFormatterFactory.getDecimalFormat(2, 3); + + public static int hrsMinSecToMilliSeconds(String hrsMinSec) { + String[] segements = hrsMinSec.split(":"); + int milliseconds = 0; + switch (segements.length) { + case 3: + String hours = segements[0].trim(); + milliseconds = Integer.valueOf(hours) * 60 * 60 * 1000; + case 2: + String minutes = segements[segements.length - 2].trim(); + milliseconds = milliseconds + Integer.valueOf(minutes) * 60 * 1000; + case 1: + String seconds = segements[segements.length - 1].trim(); + milliseconds = milliseconds + Math.round(Float.valueOf(seconds) * 1000); + } + return milliseconds; + } + + public static String milliSecondsToHrsMinSec(int milliseconds) { + int hours = (milliseconds / (60 * 60 * 1000)); + int rest = (milliseconds % (60 * 60 * 1000)); + int minutes = (rest / (60 * 1000)); + rest = (rest % (60 * 1000)); + double seconds = (double) rest / 1000; + String result = String.valueOf(hours) + ':' + zeroPaddingNumberFormat_Min.format(minutes) + ':' + zeroPaddingNumberFormat_Sec.format(seconds); + return result; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java index 43c4231ecec..4e45a335338 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java @@ -13,10 +13,14 @@ public interface MediaService extends RemoteService { Collection getMediaTracksForRace(RegattaAndRaceIdentifier regattaAndRaceIdentifier); - List getAllMediaTracks(); + Collection getAllMediaTracks(); void addMediaTrack(MediaTrack mediaTrack); void deleteMediaTrack(MediaTrack mediaTrack); + void updateStartTime(MediaTrack mediaTrack); + + void updateDuration(MediaTrack mediaTrack); + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java index 440dfe6a19c..29c4930058c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java @@ -12,10 +12,14 @@ public interface MediaServiceAsync { void getMediaTracksForRace(RegattaAndRaceIdentifier regattaAndRaceIdentifier, AsyncCallback> callback); - void getAllMediaTracks(AsyncCallback> asyncCallback); + void getAllMediaTracks(AsyncCallback> asyncCallback); void addMediaTrack(MediaTrack mediaTrack, AsyncCallback asyncCallback); void deleteMediaTrack(MediaTrack mediaTrack, AsyncCallback asyncCallback); + void updateStartTime(MediaTrack mediaTrack, AsyncCallback asyncCallback); + + void updateDuration(MediaTrack mediaTrack, AsyncCallback asyncCallback); + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 24712b7e58a..5a845a6fcc2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.ui.client; import com.google.gwt.i18n.client.Messages; +import com.google.gwt.user.cellview.client.Header; public interface StringMessages extends Messages { String helloWorld(); @@ -404,4 +405,5 @@ public interface StringMessages extends Messages { String mimeType(); String title(); String reallyRemoveMediaTrack(String mediaTrackTitle); + String duration(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 27bc5f19aeb..ddfd0c801b6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -403,4 +403,5 @@ mediaPanel=Audio & Video url=URL mimeType=MIME Type title=Title -reallyRemoveMediaTrack=Do you really want to remove media track ''{0}''? \ No newline at end of file +reallyRemoveMediaTrack=Do you really want to remove media track ''{0}''? +duration=Duration (h:mm:ss.ms) \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 0cbc07b58b4..1d44f386535 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -404,4 +404,5 @@ mediaPanel=Audio & Video url=URL mimeType=MIME Type title=Titel -reallyRemoveMediaTrack=Möchten Sie ''{0}'' wirklich entfernen? \ No newline at end of file +reallyRemoveMediaTrack=Möchten Sie ''{0}'' wirklich entfernen? +duration=Dauer (h:mm:ss.ms) \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java index 2c4bde6acad..dafc60deae6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java @@ -4,44 +4,59 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.Date; import java.util.List; -import java.util.logging.Logger; + +import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.persistence.media.DBMediaTrack; import com.sap.sailing.domain.persistence.media.MediaDB; import com.sap.sailing.domain.persistence.media.MediaDBFactory; +import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.gwt.ui.client.MediaService; import com.sap.sailing.gwt.ui.shared.media.MediaTrack; import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaSubType; import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaType; +import com.sap.sailing.server.RacingEventService; public class MediaServiceImpl extends RemoteServiceServlet implements MediaService { - private static final Logger logger = Logger.getLogger(MediaServiceImpl.class.getName()); +// private static final Logger logger = Logger.getLogger(MediaServiceImpl.class.getName()); DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + private ServiceTracker racingEventServiceTracker; + private static final long serialVersionUID = -8917349579281305977L; public MediaServiceImpl() { super(); + BundleContext context = Activator.getDefault(); + racingEventServiceTracker = new ServiceTracker( + context, RacingEventService.class.getName(), null); + racingEventServiceTracker.open(); } @Override public Collection getMediaTracksForRace(RegattaAndRaceIdentifier regattaAndRaceIdentifier) { - return getAllMediaTracks(); + TrackedRace trackedRace = racingEventServiceTracker.getService().getExistingTrackedRace(regattaAndRaceIdentifier); + if (trackedRace != null) { + Date raceStart = trackedRace.getStartOfTracking().asDate(); + Date raceEnd = trackedRace.getStartOfTracking().asDate(); + return createMediaTracksFromDB(mediaDB().queryOverlappingMediaTracks(raceStart , raceEnd)); + } else { + return Collections.emptyList(); + } } @Override - public List getAllMediaTracks() { + public Collection getAllMediaTracks() { List allDBMediaTracks = mediaDB().loadAllMediaTracks(); - List result = new ArrayList(); - for (DBMediaTrack dbMediaTrack : allDBMediaTracks) { - MediaTrack mediaTrack = createMediaTrackFromDB(dbMediaTrack); - result.add(mediaTrack ); - } + Collection result = createMediaTracksFromDB(allDBMediaTracks); return result; // try { // Date date = dateFormat.parse("2012-09-15 14:42:10 +0100"); @@ -56,16 +71,25 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi // } } + private Collection createMediaTracksFromDB(Collection allDBMediaTracks) { + Collection result = new ArrayList(); + for (DBMediaTrack dbMediaTrack : allDBMediaTracks) { + MediaTrack mediaTrack = createMediaTrackFromDB(dbMediaTrack); + result.add(mediaTrack ); + } + return result; + } + private MediaTrack createMediaTrackFromDB(DBMediaTrack dbMediaTrack) { MediaType mediaType = MediaType.valueOf(dbMediaTrack.mimeType); MediaSubType mediaSubType = MediaSubType.valueOf(dbMediaTrack.mimeSubType); - MediaTrack mediaTrack = new MediaTrack(dbMediaTrack.title, dbMediaTrack.url, dbMediaTrack.startTime, mediaType, mediaSubType); + MediaTrack mediaTrack = new MediaTrack(dbMediaTrack.dbId, dbMediaTrack.title, dbMediaTrack.url, dbMediaTrack.startTime, dbMediaTrack.durationInMillis, mediaType, mediaSubType); return mediaTrack; } @Override public void addMediaTrack(MediaTrack mediaTrack) { - mediaDB().insertMediaTrack(mediaTrack.title, mediaTrack.url, mediaTrack.startTime, mediaTrack.type.name(), mediaTrack.subType.name()); + mediaDB().insertMediaTrack(mediaTrack.title, mediaTrack.url, mediaTrack.startTime, mediaTrack.durationInMillis, mediaTrack.type.name(), mediaTrack.subType.name()); } private MediaDB mediaDB() { @@ -74,6 +98,16 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi @Override public void deleteMediaTrack(MediaTrack mediaTrack) { - mediaDB().deleteMediaTrack(mediaTrack.title); + mediaDB().deleteMediaTrack(mediaTrack.dbId); + } + + @Override + public void updateStartTime(MediaTrack mediaTrack) { + mediaDB().updateStartTime(mediaTrack.dbId, mediaTrack.startTime); + } + + @Override + public void updateDuration(MediaTrack mediaTrack) { + mediaDB().updateDuration(mediaTrack.dbId, mediaTrack.durationInMillis); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java index c436f7c503b..b34842c62d6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java @@ -44,9 +44,11 @@ public class MediaTrack implements IsSerializable { public Date endTime; } + public String dbId; public String title; public String url; public Date startTime; + public int durationInMillis; public MediaType type; public MediaSubType subType; @@ -54,13 +56,20 @@ public class MediaTrack implements IsSerializable { super(); } - public MediaTrack(String title, String url, Date startTime, MediaType type, MediaSubType subType) { + public MediaTrack(String title, String url, Date startTime, int durationInMillis, MediaType mediaType, MediaSubType mediaSubType) { + this(null, title, url, startTime, durationInMillis, mediaType, mediaSubType); + } + + public MediaTrack(String dbId, String title, String url, Date startTime, int durationInMillis, MediaType mediaType, + MediaSubType mediaSubType) { super(); + this.dbId = dbId; this.title = title; this.url = url; this.startTime = startTime; - this.type = type; - this.subType = subType; + this.durationInMillis = durationInMillis; + this.type = mediaType; + this.subType = mediaSubType; } } diff --git a/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF index a3292d09da5..96bc1444f1a 100644 --- a/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF @@ -12,5 +12,6 @@ Require-Bundle: com.mongodb.driver;bundle-version="2.6.2", org.hamcrest.core;bundle-version="1.1.0", org.junit4;bundle-version="4.8.2", com.sap.sailing.domain.persistence;bundle-version="1.0.0", - com.sap.sailing.gwt.ui;bundle-version="1.0.0" + com.sap.sailing.gwt.ui;bundle-version="1.0.0", + com.sap.sailing.domain.common;bundle-version="1.0.0" diff --git a/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestCRUDMedia.java b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestCRUDMedia.java deleted file mode 100644 index 0b757ac1f48..00000000000 --- a/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestCRUDMedia.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.sap.sailing.media.persistence.test; - -import java.net.UnknownHostException; -import java.util.Date; -import java.util.List; -import java.util.logging.Logger; - -import org.hamcrest.core.Is; -import org.junit.Test; - -import com.mongodb.MongoException; -import com.sap.sailing.domain.persistence.media.DBMediaTrack; -import com.sap.sailing.domain.persistence.media.MediaDB; -import com.sap.sailing.domain.persistence.media.MediaDBFactory; -import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaSubType; -import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaType; - -import static junit.framework.Assert.assertNotNull; - -import static org.junit.Assert.assertThat; - -public class TestCRUDMedia extends AbstractMongoDBTest { - - private static final Logger logger = Logger.getLogger(TestCRUDMedia.class.getName()); - - public TestCRUDMedia() throws UnknownHostException, MongoException { - super(); - } - - @Test - public void testCreateVideoTrack() { - final String videoTitle = "Test Video"; - final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; - MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); - Date date = new Date(); - String mediaType = MediaType.VIDEO.name(); - String mediaSubType = MediaSubType.ogg.name(); - mongoDB.insertMediaTrack(videoTitle, url, date, mediaType, mediaSubType); - DBMediaTrack videoTrack = mongoDB.loadMediaTrack(videoTitle); - assertNotNull(videoTrack); - assertThat(videoTrack.title, Is.is(videoTitle)); - } - - @Test(expected = RuntimeException.class) - public void testFailReadMissingVideoTrack() { - final String videoTitle = "Test Video"; - MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); - mongoDB.loadMediaTrack(videoTitle); - } - - @Test - public void testLoadAllVideoTracks() { - MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); - int testEntryCount = 10; - storeNumberOfTestMediaTracks(mongoDB, testEntryCount); - List allMediaTracks = mongoDB.loadAllMediaTracks(); - assertThat(allMediaTracks.size(), org.hamcrest.core.Is.is(testEntryCount)); - } - - private void storeNumberOfTestMediaTracks(MediaDB mongoDB, int count) { - final String videoTitleTemplate = "Test Video "; - final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; - Date date = new Date(); - String mediaType = MediaType.VIDEO.name(); - String mediaSubType = MediaSubType.ogg.name(); - - for (int i = 0; i < count; i++) { - mongoDB.insertMediaTrack(videoTitleTemplate + i, url, date, mediaType, mediaSubType); - } - } - - @Test(expected=RuntimeException.class) - public void testDeleteVideoTrack() { - //insert test object - final String videoTitle = "Test Video"; - final String url = "test"; - MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); - Date date = new Date(); - String mediaType = MediaType.VIDEO.name(); - String mediaSubType = MediaSubType.ogg.name(); - mongoDB.insertMediaTrack(videoTitle, url, date, mediaType, mediaSubType); - - //delete - mongoDB.deleteMediaTrack(videoTitle); - - //assert not exists --> RuntimeException - mongoDB.loadMediaTrack(videoTitle); - } - -} diff --git a/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestMediaDB.java b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestMediaDB.java new file mode 100644 index 00000000000..7e6c9ef6785 --- /dev/null +++ b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestMediaDB.java @@ -0,0 +1,232 @@ +package com.sap.sailing.media.persistence.test; + +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import org.hamcrest.core.Is; +import org.junit.Test; + +import com.mongodb.MongoException; +import com.sap.sailing.domain.persistence.media.DBMediaTrack; +import com.sap.sailing.domain.persistence.media.MediaDB; +import com.sap.sailing.domain.persistence.media.MediaDBFactory; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaSubType; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaType; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; + +import static org.hamcrest.core.Is.is; + +public class TestMediaDB extends AbstractMongoDBTest { + + private static final int FIFTEEN_MINUTES_IN_MILLIS = 15 * 60 * 1000; + private static final int THIRTY_MINUTES_IN_MILLIS = 30 * 60 * 1000; + private static final int ONE_HOUR_IN_MILLIS = 60 * 60 * 1000; + +// private static final Logger logger = Logger.getLogger(TestCRUDMedia.class.getName()); + + public TestMediaDB() throws UnknownHostException, MongoException { + super(); + } + + @Test + public void testCreateVideoTrack() { + final String videoTitle = "Test Video"; + final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; + MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); + Date date = new Date(); + int durationInMillis = 23; + String mediaType = MediaType.VIDEO.name(); + String mediaSubType = MediaSubType.ogg.name(); + String dbId = mongoDB.insertMediaTrack(videoTitle, url, date, durationInMillis, mediaType, mediaSubType); + DBMediaTrack videoTrack = mongoDB.loadMediaTrack(dbId); + assertNotNull(videoTrack); + assertThat(videoTrack.title, Is.is(videoTitle)); + } + + @Test(expected = RuntimeException.class) + public void testFailReadMissingVideoTrack() { + final String dbId = "non-existing id"; + MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); + mongoDB.loadMediaTrack(dbId); + } + + @Test + public void testLoadAllVideoTracks() { + MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); + int testEntryCount = 10; + storeNumberOfTestMediaTracks(mongoDB, testEntryCount); + List allMediaTracks = mongoDB.loadAllMediaTracks(); + assertThat(allMediaTracks.size(), is(testEntryCount)); + } + + private void storeNumberOfTestMediaTracks(MediaDB mongoDB, int count) { + final String videoTitleTemplate = "Test Video "; + final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; + Date date = new Date(); + int durationInMillis = 23; + String mediaType = MediaType.VIDEO.name(); + String mediaSubType = MediaSubType.ogg.name(); + + for (int i = 0; i < count; i++) { + mongoDB.insertMediaTrack(videoTitleTemplate + i, url, date, durationInMillis, mediaType, mediaSubType); + } + } + + @Test(expected=RuntimeException.class) + public void testDeleteVideoTrack() { + //insert test object + final String videoTitle = "Test Video"; + final String url = "test"; + MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); + Date date = new Date(); + int durationInMillis = 23; + String mediaType = MediaType.VIDEO.name(); + String mediaSubType = MediaSubType.ogg.name(); + String dbId = mongoDB.insertMediaTrack(videoTitle, url, date, durationInMillis, mediaType, mediaSubType); + + //delete + mongoDB.deleteMediaTrack(videoTitle); + + //assert not exists --> RuntimeException + mongoDB.loadMediaTrack(dbId); + } + + @Test + public void testUpdateStartTime() { + //insert test object + final String videoTitle = "Test Video"; + final String url = "test"; + MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); + Date originalDate = new Date(); + int durationInMillis = 23; + String mediaType = MediaType.VIDEO.name(); + String mediaSubType = MediaSubType.ogg.name(); + String dbId = mongoDB.insertMediaTrack(videoTitle, url, originalDate, durationInMillis, mediaType, mediaSubType); + + //update with new date + Date newDate = new Date(originalDate.getTime() + 1000); + mongoDB.updateStartTime(dbId, newDate); + + //assert start time is updated and everything else preserved + DBMediaTrack videoTrack = mongoDB.loadMediaTrack(dbId); + assertThat(videoTrack.startTime, is(newDate)); + assertThat(videoTrack.durationInMillis, is(durationInMillis)); + assertThat(videoTrack.title, is(videoTitle)); + assertThat(videoTrack.url, is(url)); + assertThat(videoTrack.mimeType, is(mediaType)); + assertThat(videoTrack.mimeSubType, is(mediaSubType)); + } + + + @Test + public void testQueryMediaTracksBetween_MatchingStartTimes_TrackLongerThanEndTime() { + Date startTime = new Date(); + int duration = ONE_HOUR_IN_MILLIS; + Date rangeStart = startTime; + Date rangeEnd = new Date(rangeStart.getTime() + THIRTY_MINUTES_IN_MILLIS); + + assertOverlap(startTime, duration, rangeStart, rangeEnd); + } + + @Test + public void testQueryMediaTracksBetween_MatchingStartTimes_TrackShorterThanEndTime() { + Date startTime = new Date(); + int duration = THIRTY_MINUTES_IN_MILLIS; + Date rangeStart = startTime; + Date rangeEnd = new Date(rangeStart.getTime() + ONE_HOUR_IN_MILLIS); + + assertOverlap(startTime, duration, rangeStart, rangeEnd); + } + + @Test + public void testQueryMediaTracksBetween_StartsBeforeEndsAfterRange() { + Date startTime = new Date(); + int duration = ONE_HOUR_IN_MILLIS; + Date rangeStart = new Date(startTime.getTime() + FIFTEEN_MINUTES_IN_MILLIS); + Date rangeEnd = new Date(rangeStart.getTime() + FIFTEEN_MINUTES_IN_MILLIS); + + assertOverlap(startTime, duration, rangeStart, rangeEnd); + } + + @Test + public void testQueryMediaTracksBetween_StartsAfterEndsBeforeRange() { + Date rangeStart = new Date(); + Date rangeEnd = new Date(rangeStart.getTime() + ONE_HOUR_IN_MILLIS); + Date startTime = new Date(rangeStart.getTime() + FIFTEEN_MINUTES_IN_MILLIS); + int duration = FIFTEEN_MINUTES_IN_MILLIS; + + assertOverlap(startTime, duration, rangeStart, rangeEnd); + } + + @Test + public void testQueryMediaTracksBetween_StartsAfterEndsAfterRange() { + Date rangeStart = new Date(); + Date rangeEnd = new Date(rangeStart.getTime() + THIRTY_MINUTES_IN_MILLIS); + Date startTime = new Date(rangeStart.getTime() + FIFTEEN_MINUTES_IN_MILLIS); + int duration = THIRTY_MINUTES_IN_MILLIS; + + assertOverlap(startTime, duration, rangeStart, rangeEnd); + } + + @Test + public void testQueryMediaTracksBetween_StartsBeforeEndsBeforeRange() { + Date startTime = new Date(); + int duration = THIRTY_MINUTES_IN_MILLIS; + Date rangeStart = new Date(startTime.getTime() + FIFTEEN_MINUTES_IN_MILLIS); + Date rangeEnd = new Date(rangeStart.getTime() + THIRTY_MINUTES_IN_MILLIS); + + assertOverlap(startTime, duration, rangeStart, rangeEnd); + } + + @Test + public void testQueryMediaTracksBetween_TrackEndsBeforeRange() { + Date startTime = new Date(); + int duration = THIRTY_MINUTES_IN_MILLIS; + Date rangeStart = new Date(startTime.getTime() + duration + 1); + Date rangeEnd = new Date(rangeStart.getTime() + ONE_HOUR_IN_MILLIS); + + assertNoOverlap(startTime, duration, rangeStart, rangeEnd); + } + + @Test + public void testQueryMediaTracksBetween_TrackStartsAfterRange() { + Date rangeStart = new Date(); + Date rangeEnd = new Date(rangeStart.getTime() + THIRTY_MINUTES_IN_MILLIS); + Date startTime = new Date(rangeStart.getTime() + ONE_HOUR_IN_MILLIS); + int duration = THIRTY_MINUTES_IN_MILLIS; + + assertNoOverlap(startTime, duration, rangeStart, rangeEnd); + } + + private void assertOverlap(Date startTime, int oneHourInMillis, Date rangeStart, Date rangeEnd) { + //insert test object + final String videoTitle = "Test Video"; + final String url = "test"; + MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); + String mediaType = MediaType.VIDEO.name(); + String mediaSubType = MediaSubType.ogg.name(); + String dbId = mongoDB.insertMediaTrack(videoTitle, url, startTime, oneHourInMillis, mediaType, mediaSubType); + + Collection videoTracks = mongoDB.queryOverlappingMediaTracks(rangeStart, rangeEnd); + assertThat(videoTracks.size(), is(1)); + assertThat(videoTracks.iterator().next().dbId, is(dbId)); + } + + private void assertNoOverlap(Date startTime, int oneHourInMillis, Date rangeStart, Date rangeEnd) { + //insert test object + final String videoTitle = "Test Video"; + final String url = "test"; + MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); + String mediaType = MediaType.VIDEO.name(); + String mediaSubType = MediaSubType.ogg.name(); + String dbId = mongoDB.insertMediaTrack(videoTitle, url, startTime, oneHourInMillis, mediaType, mediaSubType); + + Collection videoTracks = mongoDB.queryOverlappingMediaTracks(rangeStart, rangeEnd); + assertThat(videoTracks.size(), is(0)); + } + +} From 027e23a2258f3472dbf7350e2be2f97fbfaaa9ff Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Tue, 11 Dec 2012 16:53:01 +0100 Subject: [PATCH 04/31] Clean up effort after switching branches back and forth screwed up whole local state ?!!??! --- .../com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java | 2 +- .../java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index 82df3b033ec..a91230f67c0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -133,7 +133,7 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { raceSelectionModel.setSelection(singletonList); Timer timer = new Timer(PlayModes.Replay, 1000l); RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, this, singletonList, 5000l /* requestInterval*/); - RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, user, timer, raceSelectionModel, leaderboardName, leaderboardGroupName, + RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, user, timer, raceSelectionModel, leaderboardName, leaderboardGroupName, RaceBoardEntryPoint.this, stringMessages, userAgent, viewMode, raceTimesInfoProvider); raceBoardPanel.fillRegattas(regattas); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java index 53aa26dddec..d33c9555fd0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java @@ -4,7 +4,6 @@ import com.google.gwt.media.client.Video; import com.google.gwt.user.client.ui.DialogBox; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.VerticalPanel; -import com.sap.sailing.gwt.ui.client.MediaPlayer; import com.sap.sailing.gwt.ui.shared.media.MediaTrack; public class VideoPopup extends AbstractMediaPlayer { @@ -20,12 +19,11 @@ public class VideoPopup extends AbstractMediaPlayer { videoHolder.add(mediaControl); // HTML videoFrame = new -s // HTML(""); + // HTML(""); // // SimplePanel videoFrameHolder = new SimplePanel(); // videoFrameHolder.add(videoFrame); - popUpPanelContents.add(videoHolder); // popUpPanelContents.add(videoFrameHolder); dialogBox.setWidget(popUpPanelContents); } From 5b37de03b3184f1c641517047ffbb93327bea8fe Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Tue, 11 Dec 2012 18:21:40 +0100 Subject: [PATCH 05/31] Fixed issues with audio/video eventing --- java/com.sap.sailing.gwt.ui/RaceBoard.css | 4 ++-- .../gwt/ui/raceboard/AbstractMediaPlayer.java | 12 ++++++------ .../gwt/ui/raceboard/MediaSelectionDialog.java | 9 +++++++++ .../com/sap/sailing/gwt/ui/raceboard/VideoPopup.java | 8 +------- .../sap/sailing/gwt/ui/shared/media/MediaTrack.java | 4 ++-- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/RaceBoard.css b/java/com.sap.sailing.gwt.ui/RaceBoard.css index 992940e82a1..10e45c64ea0 100644 --- a/java/com.sap.sailing.gwt.ui/RaceBoard.css +++ b/java/com.sap.sailing.gwt.ui/RaceBoard.css @@ -240,12 +240,12 @@ img.openColumn { -webkit-border-radius: 5px 5px 5px 5px; } -.raceBoardNavigation-standaloneElement { +.raceBoardNavigation-standaloneElement:active { top: 1px; position: relative; } -.raceBoardNavigation-standaloneElement { +.raceBoardNavigation-standaloneElement:hover { background: url("images/btn-cta-orangeleft.png") repeat-x scroll center -30px transparent; cursor: pointer; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractMediaPlayer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractMediaPlayer.java index 7f2e2bf6490..a15e60d5d02 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractMediaPlayer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractMediaPlayer.java @@ -47,15 +47,15 @@ public abstract class AbstractMediaPlayer implements MediaPlayer { long mediaStartTimeInMillis = mediaTrack.startTime.getTime(); long mediaTimeInMillis = mediaStartTimeInMillis + Math.round(mediaControl.getCurrentTime() * 1000); long raceTimeInMillis = raceTime.getTime(); - long videoLaggingBehindRaceInMillis = raceTimeInMillis - mediaTimeInMillis; - if (Math.abs(videoLaggingBehindRaceInMillis) > TOLERATED_LAG_IN_MILLISECONDS) { - double videoTime = (raceTimeInMillis - mediaStartTimeInMillis) / 1000; - if (videoTime < 0) { + long mediaLaggingBehindRaceInMillis = raceTimeInMillis - mediaTimeInMillis; + if (Math.abs(mediaLaggingBehindRaceInMillis) > TOLERATED_LAG_IN_MILLISECONDS) { + double mediaTime = (raceTimeInMillis - mediaStartTimeInMillis) / 1000; + if (mediaTime < 0) { pause(); - } else if (videoTime > mediaControl.getDuration()) { + } else if (mediaTime > mediaControl.getDuration()) { pause(); } else { - mediaControl.setCurrentTime(videoTime); + mediaControl.setCurrentTime(mediaTime); } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java index beb5e08fc43..dbc6c66b063 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java @@ -88,6 +88,15 @@ public class MediaSelectionDialog implements CloseHandler { RadioButton audioItem = new RadioButton("group-name", title); audioItem.setTitle(tooltip); audioItem.setValue(audioTrack == selectedAudioTrack); + audioItem.addValueChangeHandler(new ValueChangeHandler() { + + @Override + public void onValueChange(ValueChangeEvent changeEvent) { + if (changeEvent.getValue()) { + mediaSelectionListener.audioChanged(audioTrack); + } + } + }); return audioItem; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java index d33c9555fd0..d2a319e4805 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java @@ -2,8 +2,6 @@ package com.sap.sailing.gwt.ui.raceboard; import com.google.gwt.media.client.Video; import com.google.gwt.user.client.ui.DialogBox; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.VerticalPanel; import com.sap.sailing.gwt.ui.shared.media.MediaTrack; public class VideoPopup extends AbstractMediaPlayer { @@ -13,10 +11,7 @@ public class VideoPopup extends AbstractMediaPlayer { public VideoPopup() { super(Video.createIfSupported()); this.dialogBox = new DialogBox(false, false); - VerticalPanel popUpPanelContents = new VerticalPanel(); - SimplePanel videoHolder = new SimplePanel(); if (mediaControl != null) { - videoHolder.add(mediaControl); // HTML videoFrame = new // HTML(""); @@ -24,8 +19,7 @@ public class VideoPopup extends AbstractMediaPlayer { // SimplePanel videoFrameHolder = new SimplePanel(); // videoFrameHolder.add(videoFrame); - // popUpPanelContents.add(videoFrameHolder); - dialogBox.setWidget(popUpPanelContents); + dialogBox.setWidget(mediaControl); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java index 9063ddcfd3b..238cc85f931 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java @@ -8,7 +8,7 @@ public class MediaTrack implements IsSerializable { public enum MimeFileType { - mp4(MediaType.VIDEO, MediaSubType.mpeg), ogv(MediaType.VIDEO, MediaSubType.ogg), qt(MediaType.VIDEO, MediaSubType.quicktime), mp3(MediaType.AUDIO, MediaSubType.mpeg), ogg(MediaType.AUDIO, MediaSubType.ogg), aac(MediaType.AUDIO, MediaSubType.aac); + mp4(MediaType.VIDEO, MediaSubType.mpeg), ogv(MediaType.VIDEO, MediaSubType.ogg), qt(MediaType.VIDEO, MediaSubType.quicktime), mp3(MediaType.AUDIO, MediaSubType.mpeg), ogg(MediaType.AUDIO, MediaSubType.ogg), aac(MediaType.AUDIO, MediaSubType.aac), webm(MediaType.VIDEO, MediaSubType.webm); public final MediaType mediaType; public final MediaSubType mediaSubType; @@ -29,7 +29,7 @@ public class MediaTrack implements IsSerializable { } public enum MediaSubType { - ogg, mpeg, x_aiff, quicktime, aac; + ogg, mpeg, x_aiff, quicktime, aac, webm; public String toString() { return name().replace('_', '-'); From 4d1f544af1eb7edd23d7169c81f4595f415ea6b4 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Tue, 11 Dec 2012 20:18:54 +0100 Subject: [PATCH 06/31] Made configuration dialog non-modal --- .../com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java index dbc6c66b063..a25d1c64db1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java @@ -35,7 +35,7 @@ public class MediaSelectionDialog implements CloseHandler { MediaSelectionDialog(MediaSelectionListener mediaSelectionListener) { this.mediaSelectionListener = mediaSelectionListener; - dialogControl = new DialogBox(true, true); + dialogControl = new DialogBox(true, false); dialogControl.setText("Select Playback Media"); dialogControl.addCloseHandler(this); } From de519fe1cd4a1d2a70c3980a36f20cf71774b113 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Wed, 12 Dec 2012 19:43:13 +0100 Subject: [PATCH 07/31] - Added a checkbox to switch on/off a default audio and video (at the moment simply the first items of the audio and video lists) - Changed the column type for the delete action in the media admin panel. --- .../gwt/ui/adminconsole/MediaPanel.java | 22 +++-- .../gwt/ui/raceboard/MediaSelector.java | 97 +++++++++++++++++-- .../gwt/ui/raceboard/RaceBoardPanel.java | 4 +- 3 files changed, 104 insertions(+), 19 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java index 6f8106aa101..43770ac3b71 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java @@ -3,7 +3,6 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.Collection; import java.util.Comparator; -import com.google.gwt.cell.client.ActionCell; import com.google.gwt.cell.client.EditTextCell; import com.google.gwt.cell.client.FieldUpdater; import com.google.gwt.core.client.GWT; @@ -15,7 +14,6 @@ import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler; -import com.google.gwt.user.cellview.client.IdentityColumn; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; @@ -330,16 +328,20 @@ public class MediaPanel extends FlowPanel { mediaTracksTable.setColumnWidth(durationColumn, 100, Unit.PCT); // delete action - Column deleteActionColumn = new IdentityColumn(new ActionCell("X", new ActionCell.Delegate() { + ImagesBarColumn mediaActionColumn = new ImagesBarColumn( + new MediaActionBarCell(stringMessages)); + mediaActionColumn.setFieldUpdater(new FieldUpdater() { @Override - public void execute(MediaTrack mediaTrack) { - if (Window.confirm(stringMessages.reallyRemoveMediaTrack(mediaTrack.title))) { - removeMediaTrack(mediaTrack); - } + public void update(int index, MediaTrack mediaTrack, String value) { + if (MediaActionBarCell.ACTION_REMOVE.equals(value)) { + if (Window.confirm(stringMessages.reallyRemoveMediaTrack(mediaTrack.title))) { + removeMediaTrack(mediaTrack); + } + } } - })); - mediaTracksTable.addColumn(deleteActionColumn, stringMessages.delete()); - mediaTracksTable.setColumnWidth(deleteActionColumn, 5, Unit.PCT); + }); + mediaTracksTable.addColumn(mediaActionColumn, stringMessages.delete()); + mediaTracksTable.setColumnWidth(mediaActionColumn, 5, Unit.PCT); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java index b527dc7caca..c7ac2c9f400 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java @@ -10,8 +10,11 @@ import java.util.Set; import com.google.gwt.dom.client.Style; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.gwt.ui.client.ErrorReporter; @@ -28,7 +31,9 @@ import com.sap.sailing.gwt.ui.shared.media.MediaTrack; public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateListener, TimeListener, AsyncCallback>, MediaSelectionListener { + private final CheckBox toggleMediaButton; private final Button manageMediaButton; + private final MediaSelectionDialog mediaSelectionDialog; private MediaPlayer audioPlayer; @@ -46,7 +51,8 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi mediaSelectionDialog = new MediaSelectionDialog(this); - manageMediaButton = new Button("Audio & Video"); + manageMediaButton = new Button(); + manageMediaButton.setEnabled(false); manageMediaButton.addClickHandler(new ClickHandler() { @Override @@ -56,17 +62,78 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi } else { MediaTrack playingAudioTrack = audioPlayer != null ? audioPlayer.getMediaTrack() : null; Set playingVideoTracks = videoPlayers.keySet(); - mediaSelectionDialog.show(videoTracks, playingVideoTracks, audioTracks, playingAudioTrack, manageMediaButton); + mediaSelectionDialog.show(videoTracks, playingVideoTracks, audioTracks, playingAudioTrack, toggleMediaButton); } } }); - manageMediaButton.addStyleName("raceBoardNavigation-standaloneElement"); + manageMediaButton.addStyleName("raceBoardNavigation-settingsButton"); manageMediaButton.getElement().getStyle().setFloat(Style.Float.LEFT); manageMediaButton.setTitle("Configure Media"); + toggleMediaButton = new CheckBox("Audio & Video"); + toggleMediaButton.setEnabled(false); + toggleMediaButton.addStyleName("raceBoardNavigation-innerElement"); + toggleMediaButton.getElement().getStyle().setFloat(Style.Float.LEFT); + toggleMediaButton.addValueChangeHandler(new ValueChangeHandler() { + + @Override + public void onValueChange(ValueChangeEvent event) { + + if (toggleMediaButton.getValue()) { + playDefault(); + } else { + stopAll(); + } + + } + + }); } + private void playDefault() { + MediaTrack defaultVideo = getDefaultVideo(); + if (defaultVideo != null) { + videoSelected(defaultVideo ); + } + MediaTrack defaultAudio = getDefaultAudio(); + if (defaultAudio != null) { + audioChanged(defaultAudio); + } + } + + private MediaTrack getDefaultAudio() { + if (!audioTracks.isEmpty()) { + return audioTracks.iterator().next(); + } else { + //TODO: determine best fitting audio, e.g. best overlap with race + return getDefaultVideo(); + } + } + + private MediaTrack getDefaultVideo() { + if (!videoTracks.isEmpty()) { + return videoTracks.iterator().next(); + } else { + //TODO: determine best fitting video, e.g. best overlap with race + return null; + } + + } + + private void stopAll() { + if (audioPlayer != null) { + audioPlayer.pause(); + audioPlayer = null; + } + for (VideoPopup videoPlayer : videoPlayers.values()) { + videoPlayer.pause(); + videoPlayer.hide(); + } + videoPlayers.clear(); + } + + @Override public void playStateChanged(PlayStates playState, PlayModes playMode) { this.currentPlayState = playState; @@ -131,10 +198,6 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi } } - public Widget widget() { - return manageMediaButton; - } - @Override public void onFailure(Throwable caught) { errorReporter @@ -143,7 +206,6 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi @Override public void onSuccess(Collection mediaTracks) { - manageMediaButton.setEnabled(mediaTracks.size() > 0); for (MediaTrack mediaTrack : mediaTracks) { switch (mediaTrack.type) { case VIDEO: @@ -153,6 +215,8 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi break; } } + manageMediaButton.setEnabled(mediaTracks.size() > 0); + toggleMediaButton.setEnabled(mediaTracks.size() > 0); } @Override @@ -185,6 +249,9 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi } else { assignNewAudioPlayer(audioTrack); } + + updateToggleButton(); + } @Override @@ -207,6 +274,9 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi } else { //nothing changed } + + updateToggleButton(); + } @Override @@ -217,8 +287,15 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi assignNewAudioPlayer(removedVideoPlayer.getMediaTrack()); } removedVideoPlayer.hide(); + + updateToggleButton(); + } + private void updateToggleButton() { + toggleMediaButton.setValue((audioPlayer != null) || (!videoPlayers.isEmpty())); + } + private void assignNewAudioPlayer(MediaTrack audioTrack) { if (audioTrack != null) { audioPlayer = new AudioControl(); @@ -245,4 +322,8 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi } } + public Widget[] widgets() { + return new Widget[] {toggleMediaButton, manageMediaButton}; + } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index cc9266e5b1b..f2e975405b8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -183,7 +183,9 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS timer.addPlayStateListener(mediaSelector); timer.addTimeListener(mediaSelector); mediaService.getMediaTracksForRace(selectedRaceIdentifier, mediaSelector); - componentsNavigationPanel.add(mediaSelector.widget()); + for (Widget widget : mediaSelector.widgets()) { + componentsNavigationPanel.add(widget); + } } @SuppressWarnings("unused") From e50acd41ba6809feb00222537849d742a2613f54 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Fri, 14 Dec 2012 10:04:43 +0100 Subject: [PATCH 08/31] - added missing class --- .../ui/adminconsole/MediaActionBarCell.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaActionBarCell.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaActionBarCell.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaActionBarCell.java new file mode 100644 index 00000000000..f467f6d147a --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaActionBarCell.java @@ -0,0 +1,24 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.Arrays; + +import com.google.gwt.core.client.GWT; +import com.sap.sailing.gwt.ui.client.StringMessages; + +public class MediaActionBarCell extends ImagesBarCell { + + public static final String ACTION_REMOVE = "ACTION_REMOVE"; + private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class); + private final StringMessages stringMessages; + + public MediaActionBarCell(StringMessages stringMessages) { + super(); + this.stringMessages = stringMessages; + } + + @Override + protected Iterable getImageSpecs() { + return Arrays.asList(new ImageSpec(ACTION_REMOVE, + stringMessages.actionRemove(), makeImagePrototype(resources.removeIcon()))); + } +} From ee00c5948cc8b5ba3d3edb82a99a00b6230ac465 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Sun, 6 Jan 2013 12:20:32 +0100 Subject: [PATCH 09/31] Changed add-media dialog a bit: - added support for duration (and start time which might only make sense for live streaming) - removed the posssibility to change anything else than URL and title --- .../gwt/ui/adminconsole/MediaPanel.java | 61 +------ .../gwt/ui/adminconsole/MediaTrackDialog.java | 157 +++++++++--------- .../gwt/ui/adminconsole/TimeFormatUtil.java | 12 +- .../gwt/ui/raceboard/MediaSelector.java | 4 +- .../gwt/ui/shared/media/MediaTrack.java | 22 ++- .../media/persistence/test/TestMediaDB.java | 12 +- 6 files changed, 119 insertions(+), 149 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java index 43770ac3b71..db33f2365ab 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java @@ -9,8 +9,6 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.i18n.client.DateTimeFormat; -import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler; @@ -37,10 +35,6 @@ import com.sap.sailing.gwt.ui.shared.media.MediaTrack; */ public class MediaPanel extends FlowPanel { -// private static final DateTimeFormat TIME_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.HOUR24_MINUTE_SECOND); - private static final DateTimeFormat DATETIME_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.DATE_TIME_MEDIUM); - // private Calendar calender = new GregorianCalendar(); - private final MediaServiceAsync mediaService; private final ErrorReporter errorReporter; private final StringMessages stringMessages; @@ -208,62 +202,11 @@ public class MediaPanel extends FlowPanel { }); mediaTracksTable.setColumnWidth(urlColumn, 100, Unit.PCT); - // media types - // final Category[] categories = ContactDatabase.get().queryCategories(); - // List categoryNames = new ArrayList(); - // for (Category category : categories) { - // categoryNames.add(category.getDisplayName()); - // } - // SelectionCell categoryCell = new SelectionCell(categoryNames); - // Column categoryColumn = new Column(categoryCell) { - // @Override - // public String getValue(ContactInfo object) { - // return object.getCategory().getDisplayName(); - // } - // }; - // cellTable.addColumn(categoryColumn, constants.cwCellTableColumnCategory()); - // categoryColumn.setFieldUpdater(new FieldUpdater() { - // public void update(int index, ContactInfo object, String value) { - // for (Category category : categories) { - // if (category.getDisplayName().equals(value)) { - // object.setCategory(category); - // } - // } - // ContactDatabase.get().refreshDisplays(); - // } - // }); - // cellTable.setColumnWidth(categoryColumn, 130, Unit.PX); - - // start date - TODO: provide convenient date & time picker. Not provided by standard gwt and also not even a - // Calender util is available on the client. - // Column startDateColumn = new Column(new DatePickerCell()) { - // @Override - // public Date getValue(MediaTrackDTO mediaTrack) { - // return mediaTrack.startTime; - // } - // }; - // startDateColumn.setSortable(true); - // sortHandler.setComparator(startDateColumn, new Comparator() { - // public int compare(MediaTrackDTO mediaTrack1, MediaTrackDTO mediaTrack2) { - // return mediaTrack1.startTime.compareTo(mediaTrack2.startTime); - // } - // }); - // startDateColumn.setFieldUpdater(new FieldUpdater() { - // public void update(int index, MediaTrackDTO mediaTrack, Date newDate) { - // // Called when the user changes the value. - // calender.setTime(newDate); - // mediaTrack.startTime = newDate; - // mediaTrackListDataProvider.refresh(); - // } - // }); - // mediaTracksTable.addColumn(startDateColumn, stringMessages.startDate()); - // mediaTracksTable.setColumnWidth(startDateColumn, 30, Unit.PCT); - // start time Column startTimeColumn = new Column(new EditTextCell()) { @Override public String getValue(MediaTrack mediaTrack) { - return mediaTrack.startTime == null ? "" : DATETIME_FORMAT.format(mediaTrack.startTime); + return mediaTrack.startTime == null ? "" : mediaTrack.startTimeToString(); } }; startTimeColumn.setSortable(true); @@ -275,7 +218,7 @@ public class MediaPanel extends FlowPanel { startTimeColumn.setFieldUpdater(new FieldUpdater() { public void update(int index, MediaTrack mediaTrack, String newStartTime) { // Called when the user changes the value. - mediaTrack.startTime = DATETIME_FORMAT.parse(newStartTime); + mediaTrack.startTime = TimeFormatUtil.DATETIME_FORMAT.parse(newStartTime); mediaService.updateStartTime(mediaTrack, new AsyncCallback() { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java index 200057aa14e..02e5f25d72b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java @@ -2,21 +2,19 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.Date; -import com.google.gwt.dom.client.VideoElement; +import com.google.gwt.dom.client.MediaElement; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; -import com.google.gwt.media.client.Video; +import com.google.gwt.media.client.Audio; +import com.google.gwt.media.client.MediaBase; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; -import com.google.gwt.user.datepicker.client.DateBox; import com.sap.sailing.gwt.ui.client.DataEntryDialog; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.media.MediaTrack; -import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaSubType; -import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaType; import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MimeFileType; public class MediaTrackDialog extends DataEntryDialog { @@ -33,14 +31,18 @@ public class MediaTrackDialog extends DataEntryDialog { }; private final StringMessages stringMessages; - - private TextBox nameBox; - - private DateBox startTimeBox; + + private MediaTrack mediaTrack = new MediaTrack(); private TextBox urlBox; + + private TextBox titleBox; - private TextBox mimeTypeBox; + private Label startTimeLabel; + + private Label mimeTypeLabel; + + private Label durationLabel; public MediaTrackDialog(StringMessages stringMessages, DialogCallback dialogCallback) { super("Media Track", "", stringMessages.ok(), stringMessages.cancel(), MEDIA_TRACK_VALIDATOR, dialogCallback); @@ -49,73 +51,55 @@ public class MediaTrackDialog extends DataEntryDialog { @Override protected MediaTrack getResult() { - MediaType mediaType; - MediaSubType mediaSubType; - try { - String mimeType = mimeTypeBox.getValue(); - MimeFileType mimeFileType = MimeFileType.valueOf(mimeType); - mediaType = mimeFileType.mediaType; - mediaSubType = mimeFileType.mediaSubType; - } catch (IllegalArgumentException e) { - // ignore. TODO: Somehow put it into the error message. - // throw new IllegalArgumentException("Unsupported media type '" + mimeType + "'.", e); - mediaType = null; - mediaSubType = null; - } - String title = nameBox.getValue(); - String url = urlBox.getValue(); - Date startTime = startTimeBox.getValue(); - int durationInMillis = getMediaDuration(url, mediaType, mediaSubType); - MediaTrack result = new MediaTrack(title, url, startTime, durationInMillis, mediaType, mediaSubType); - return result; + return mediaTrack; } - - private int getMediaDuration(String url, MediaType mediaType, MediaSubType mediaSubType) { - if (mediaType != null) { - switch (mediaType) { - case VIDEO: - Video videoControl = Video.createIfSupported(); - if (videoControl != null) { - videoControl.setPreload("true"); - videoControl.setSrc(url); - VideoElement videoElement = videoControl.getVideoElement(); - int result = (int) Math.round(videoElement.getDuration()); - videoElement.load(); - videoElement.play(); - - long timeout = System.currentTimeMillis() + 5 * 1000; - while (System.currentTimeMillis() < timeout) { - if (videoElement.getReadyState() > 0) { - return (int) Math.round(videoElement.getDuration() * 1000); - } - } - return result; - } - default: - return 0; - } - } else { - return 0; + + + private void loadMediaDuration() { + MediaBase mediaBase = Audio.createIfSupported(); + if (mediaBase != null) { + addLoadMetadataHandler(mediaBase.getMediaElement()); + mediaBase.setPreload(MediaElement.PRELOAD_METADATA); + mediaBase.setSrc(mediaTrack.url); + mediaBase.load(); } } + native void addLoadMetadataHandler(MediaElement mediaElement) /*-{ + var that = this; + mediaElement.addEventListener('loadedmetadata', function() { + that.@com.sap.sailing.gwt.ui.adminconsole.MediaTrackDialog::loadedmetadata(Lcom/google/gwt/dom/client/MediaElement;)(mediaElement); + }); + }-*/; + + public void loadedmetadata(MediaElement mediaElement) { + double startOffsetTime = mediaElement.getStartOffsetTime(); + mediaTrack.startTime = Double.isNaN(startOffsetTime) ? null : new Date(Math.round(startOffsetTime * 1000)); + mediaTrack.durationInMillis = (int) Math.round(mediaElement.getDuration() * 1000); + refreshUI(); + } + + @Override protected Widget getAdditionalWidget() { VerticalPanel mainPanel = new VerticalPanel(); - Grid formGrid = new Grid(4, 2); + Grid formGrid = new Grid(5, 2); formGrid.setCellSpacing(3); - formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":")); - nameBox = createUrlBox(); - formGrid.setWidget(0, 1, nameBox); - formGrid.setWidget(1, 0, new Label(stringMessages.mimeType() + ":")); - mimeTypeBox = createUrlBox(); - formGrid.setWidget(1, 1, mimeTypeBox); - formGrid.setWidget(2, 0, new Label(stringMessages.url() + ":")); + formGrid.setWidget(0, 0, new Label(stringMessages.url() + ":")); urlBox = createUrlBox(); - formGrid.setWidget(2, 1, urlBox); + formGrid.setWidget(0, 1, urlBox); + formGrid.setWidget(1, 0, new Label(stringMessages.name() + ":")); + titleBox = createTextBox(null); + formGrid.setWidget(1, 1, titleBox); + formGrid.setWidget(2, 0, new Label(stringMessages.mimeType() + ":")); + mimeTypeLabel = new Label(); + formGrid.setWidget(2, 1, mimeTypeLabel); formGrid.setWidget(3, 0, new Label(stringMessages.startTime() + ":")); - startTimeBox = createDateBox(new Date().getTime(), 16); - formGrid.setWidget(3, 1, startTimeBox); + startTimeLabel = new Label(); + formGrid.setWidget(3, 1, startTimeLabel); + formGrid.setWidget(4, 0, new Label(stringMessages.duration() + ":")); + durationLabel = new Label(); + formGrid.setWidget(4, 1, durationLabel); mainPanel.add(formGrid); return mainPanel; } @@ -126,23 +110,46 @@ public class MediaTrackDialog extends DataEntryDialog { @Override public void onChange(ChangeEvent event) { - deriveNameAndMimeType(); + updateFromUrl(); } }); return result; } - protected void deriveNameAndMimeType() { - String url = urlBox.getValue(); + protected void updateFromUrl() { + mediaTrack.url = urlBox.getValue(); + loadMediaDuration(); - String lastPathSegment = url.substring(url.lastIndexOf('/') + 1); + String lastPathSegment = mediaTrack.url.substring(mediaTrack.url.lastIndexOf('/') + 1); int dotPos = lastPathSegment.lastIndexOf('.'); - String fileEnding = lastPathSegment.substring(dotPos + 1); - String fileName = lastPathSegment.substring(0, dotPos); + if (dotPos >= 0) { + mediaTrack.title = lastPathSegment.substring(0, dotPos); + String fileEnding = lastPathSegment.substring(dotPos + 1); + + try { + MimeFileType mimeFileType = MimeFileType.valueOf(fileEnding); + mediaTrack.type = mimeFileType.mediaType; + mediaTrack.subType = mimeFileType.mediaSubType; + } catch (IllegalArgumentException e) { + // ignore. TODO: Somehow put it into the error message. + // throw new IllegalArgumentException("Unsupported media type '" + mimeType + "'.", e); + mediaTrack.type = null; + mediaTrack.subType = null; + } + } else { + mediaTrack.title = mediaTrack.url; + mediaTrack.type = null; + mediaTrack.subType = null; + } + refreshUI(); + } - nameBox.setValue(fileName, DONT_FIRE_EVENTS); - mimeTypeBox.setValue(fileEnding, DONT_FIRE_EVENTS); + private void refreshUI() { + titleBox.setValue(mediaTrack.title, DONT_FIRE_EVENTS); + mimeTypeLabel.setText(mediaTrack.typeToString()); + startTimeLabel.setText(mediaTrack.startTimeToString()); + durationLabel.setText(TimeFormatUtil.milliSecondsToHrsMinSec(mediaTrack.durationInMillis)); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimeFormatUtil.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimeFormatUtil.java index a814ae873f5..7494b473cc1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimeFormatUtil.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimeFormatUtil.java @@ -1,10 +1,14 @@ package com.sap.sailing.gwt.ui.adminconsole; +import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.i18n.client.NumberFormat; +import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; import com.sap.sailing.gwt.ui.client.NumberFormatterFactory; public class TimeFormatUtil { + public static final DateTimeFormat DATETIME_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.DATE_TIME_MEDIUM); + private static NumberFormat zeroPaddingNumberFormat_Min = NumberFormatterFactory.getDecimalFormat(2, 0); private static NumberFormat zeroPaddingNumberFormat_Sec = NumberFormatterFactory.getDecimalFormat(2, 3); @@ -25,10 +29,10 @@ public class TimeFormatUtil { return milliseconds; } - public static String milliSecondsToHrsMinSec(int milliseconds) { - int hours = (milliseconds / (60 * 60 * 1000)); - int rest = (milliseconds % (60 * 60 * 1000)); - int minutes = (rest / (60 * 1000)); + public static String milliSecondsToHrsMinSec(long milliseconds) { + long hours = (milliseconds / (60 * 60 * 1000)); + long rest = (milliseconds % (60 * 60 * 1000)); + long minutes = (rest / (60 * 1000)); rest = (rest % (60 * 1000)); double seconds = (double) rest / 1000; String result = String.valueOf(hours) + ':' + zeroPaddingNumberFormat_Min.format(minutes) + ':' + zeroPaddingNumberFormat_Sec.format(seconds); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java index c7ac2c9f400..978ed3dbef8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java @@ -208,9 +208,9 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi public void onSuccess(Collection mediaTracks) { for (MediaTrack mediaTrack : mediaTracks) { switch (mediaTrack.type) { - case VIDEO: + case video: videoTracks.add(mediaTrack); - case AUDIO: + case audio: audioTracks.add(mediaTrack); break; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java index 238cc85f931..e3006ef13ba 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java @@ -3,12 +3,18 @@ package com.sap.sailing.gwt.ui.shared.media; import java.util.Date; import com.google.gwt.user.client.rpc.IsSerializable; +import com.sap.sailing.gwt.ui.adminconsole.TimeFormatUtil; +/** + * See http://my.opera.com/core/blog/2010/03/03/everything-you-need-to-know-about-html5-video-and-audio-2 + * @author D047974 + * + */ public class MediaTrack implements IsSerializable { public enum MimeFileType { - mp4(MediaType.VIDEO, MediaSubType.mpeg), ogv(MediaType.VIDEO, MediaSubType.ogg), qt(MediaType.VIDEO, MediaSubType.quicktime), mp3(MediaType.AUDIO, MediaSubType.mpeg), ogg(MediaType.AUDIO, MediaSubType.ogg), aac(MediaType.AUDIO, MediaSubType.aac), webm(MediaType.VIDEO, MediaSubType.webm); + mp4(MediaType.video, MediaSubType.mpeg), ogv(MediaType.video, MediaSubType.ogg), qt(MediaType.video, MediaSubType.quicktime), mp3(MediaType.audio, MediaSubType.mpeg), ogg(MediaType.audio, MediaSubType.ogg), aac(MediaType.audio, MediaSubType.aac), webm(MediaType.video, MediaSubType.webm); public final MediaType mediaType; public final MediaSubType mediaSubType; @@ -25,7 +31,7 @@ public class MediaTrack implements IsSerializable { } public enum MediaType { - AUDIO, VIDEO; + audio, video; } public enum MediaSubType { @@ -73,7 +79,17 @@ public class MediaTrack implements IsSerializable { } public String toString() { - return title + " - " + url + " [" + type + '/' + subType + ']' + startTime + " [" + durationInMillis / 1000d + "s]"; + return title + " - " + url + " [" + typeToString() + ']' + startTimeToString() + " [" + TimeFormatUtil.milliSecondsToHrsMinSec(durationInMillis) + ']'; + } + + public String typeToString() { + String typeString = type == null ? "undefined" : type.name(); + String subtypeString = subType == null ? "undefined" : subType.toString(); + return typeString + '/' + subtypeString ; + } + + public String startTimeToString() { + return startTime == null ? "undefined" : TimeFormatUtil.DATETIME_FORMAT.format(startTime); } } diff --git a/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestMediaDB.java b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestMediaDB.java index 7e6c9ef6785..da52fe3725f 100644 --- a/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestMediaDB.java +++ b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestMediaDB.java @@ -39,7 +39,7 @@ public class TestMediaDB extends AbstractMongoDBTest { MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); Date date = new Date(); int durationInMillis = 23; - String mediaType = MediaType.VIDEO.name(); + String mediaType = MediaType.video.name(); String mediaSubType = MediaSubType.ogg.name(); String dbId = mongoDB.insertMediaTrack(videoTitle, url, date, durationInMillis, mediaType, mediaSubType); DBMediaTrack videoTrack = mongoDB.loadMediaTrack(dbId); @@ -68,7 +68,7 @@ public class TestMediaDB extends AbstractMongoDBTest { final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; Date date = new Date(); int durationInMillis = 23; - String mediaType = MediaType.VIDEO.name(); + String mediaType = MediaType.video.name(); String mediaSubType = MediaSubType.ogg.name(); for (int i = 0; i < count; i++) { @@ -84,7 +84,7 @@ public class TestMediaDB extends AbstractMongoDBTest { MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); Date date = new Date(); int durationInMillis = 23; - String mediaType = MediaType.VIDEO.name(); + String mediaType = MediaType.video.name(); String mediaSubType = MediaSubType.ogg.name(); String dbId = mongoDB.insertMediaTrack(videoTitle, url, date, durationInMillis, mediaType, mediaSubType); @@ -103,7 +103,7 @@ public class TestMediaDB extends AbstractMongoDBTest { MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); Date originalDate = new Date(); int durationInMillis = 23; - String mediaType = MediaType.VIDEO.name(); + String mediaType = MediaType.video.name(); String mediaSubType = MediaSubType.ogg.name(); String dbId = mongoDB.insertMediaTrack(videoTitle, url, originalDate, durationInMillis, mediaType, mediaSubType); @@ -207,7 +207,7 @@ public class TestMediaDB extends AbstractMongoDBTest { final String videoTitle = "Test Video"; final String url = "test"; MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); - String mediaType = MediaType.VIDEO.name(); + String mediaType = MediaType.video.name(); String mediaSubType = MediaSubType.ogg.name(); String dbId = mongoDB.insertMediaTrack(videoTitle, url, startTime, oneHourInMillis, mediaType, mediaSubType); @@ -221,7 +221,7 @@ public class TestMediaDB extends AbstractMongoDBTest { final String videoTitle = "Test Video"; final String url = "test"; MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); - String mediaType = MediaType.VIDEO.name(); + String mediaType = MediaType.video.name(); String mediaSubType = MediaSubType.ogg.name(); String dbId = mongoDB.insertMediaTrack(videoTitle, url, startTime, oneHourInMillis, mediaType, mediaSubType); From 560d9ac045eccbc726f8f821b795d4d9e79a9784 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Wed, 9 Jan 2013 18:46:34 +0100 Subject: [PATCH 10/31] Running version with browser windows-based video player. Still issue with Chrome not muting video playbacks. --- .../persistence/media/DBMediaTrack.java | 4 +- .../domain/persistence/media/MediaDB.java | 2 +- .../persistence/media/impl/DbNames.java | 2 +- .../persistence/media/impl/MediaDBImpl.java | 20 +- .../com.google.gwt.eclipse.core.prefs | 3 +- .../META-INF/MANIFEST.MF | 7 +- java/com.sap.sailing.gwt.ui/VideoPopup.html | 13 ++ .../adminconsole/AdminConsoleEntryPoint.java | 6 +- .../gwt/ui/adminconsole/MediaPanel.java | 7 +- .../gwt/ui/adminconsole/MediaTrackDialog.java | 19 +- .../sailing/gwt/ui/client/MediaPlayer.java | 14 +- .../gwt/ui/raceboard/AbstractMediaPlayer.java | 92 ++------ .../gwt/ui/raceboard/AudioControl.java | 125 +++++++++- .../ui/raceboard/MediaSelectionDialog.java | 29 ++- .../gwt/ui/raceboard/MediaSelector.java | 194 ++++++++++------ .../gwt/ui/raceboard/VideoControl.java | 150 ++++++++++++ .../sailing/gwt/ui/raceboard/VideoPopup.java | 41 ---- .../gwt/ui/server/MediaServiceImpl.java | 15 +- .../gwt/ui/shared/media/MediaTrack.java | 32 +-- .../gwt/ui/video/VideoPopupWindow.java | 214 ++++++++++++++++++ .../com/sap/sailing/gwt/ui/VideoPopup.gwt.xml | 8 + .../media/persistence/test/TestMediaDB.java | 36 ++- .../SailingServer (Proxy).launch | 2 +- 23 files changed, 749 insertions(+), 286 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/VideoPopup.html create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoControl.java delete mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/video/VideoPopupWindow.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/VideoPopup.gwt.xml diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java index 9070c8e750c..d0b88798320 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java @@ -10,9 +10,8 @@ public class DBMediaTrack { public final Date startTime; public final int durationInMillis; public final String mimeType; - public final String mimeSubType; - public DBMediaTrack(String dbId, String title, String url, Date startTime, int duration, String mimeType, String mimeSubType) { + public DBMediaTrack(String dbId, String title, String url, Date startTime, int duration, String mimeType) { super(); this.dbId = dbId; this.title = title; @@ -20,6 +19,5 @@ public class DBMediaTrack { this.startTime = startTime; this.durationInMillis = duration; this.mimeType = mimeType; - this.mimeSubType = mimeSubType; } } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java index cea81a8c049..4d30c6bf801 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java @@ -12,7 +12,7 @@ import java.util.List; */ public interface MediaDB { - String insertMediaTrack(String title, String url, Date startTime, int durationInMillis, String mediaType, String mediaSubType); + String insertMediaTrack(String title, String url, Date startTime, int durationInMillis, String mimeType); DBMediaTrack loadMediaTrack(String dbId); diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/DbNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/DbNames.java index 60b59e84fa4..54e31d0c245 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/DbNames.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/DbNames.java @@ -3,7 +3,7 @@ package com.sap.sailing.domain.persistence.media.impl; public class DbNames { public enum Fields { - _id, MEDIA_TITLE, MEDIA_URL, STARTTIME, DURATION_IN_MILLIS, MIME_TYPE, MIME_SUBTYPE; + _id, MEDIA_TITLE, MEDIA_URL, STARTTIME, DURATION_IN_MILLIS, MIME_TYPE; } public enum Collections { diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java index f3c42ad38c7..168ce87270a 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java @@ -24,23 +24,27 @@ import com.sap.sailing.domain.persistence.media.MediaDB; */ public class MediaDBImpl implements MediaDB { + private static final int SORT_ASCENDING = 1; // private static Logger logger = Logger.getLogger(MediaDBImpl.class.getName()); private final DB database; + private final BasicDBObject sortByStartTimeAndTitle; public MediaDBImpl(DB database) { super(); this.database = database; + sortByStartTimeAndTitle = new BasicDBObject(); + sortByStartTimeAndTitle.put(DbNames.Fields.STARTTIME.name(), SORT_ASCENDING); + sortByStartTimeAndTitle.put(DbNames.Fields.MEDIA_TITLE.name(), SORT_ASCENDING); } @Override - public String insertMediaTrack(String videoTitle, String url, Date startTime, int durationInMillis, String mediaType, String mediaSubType) { + public String insertMediaTrack(String videoTitle, String url, Date startTime, int durationInMillis, String mimeType) { BasicDBObject dbVideo = new BasicDBObject(); dbVideo.put(DbNames.Fields.MEDIA_TITLE.name(), videoTitle); dbVideo.put(DbNames.Fields.MEDIA_URL.name(), url); dbVideo.put(DbNames.Fields.STARTTIME.name(), startTime); dbVideo.put(DbNames.Fields.DURATION_IN_MILLIS.name(), durationInMillis); - dbVideo.put(DbNames.Fields.MIME_TYPE.name(), mediaType); - dbVideo.put(DbNames.Fields.MIME_SUBTYPE.name(), mediaSubType); + dbVideo.put(DbNames.Fields.MIME_TYPE.name(), mimeType); DBCollection dbVideos = getVideoCollection(); dbVideos.insert(dbVideo); return ((ObjectId) dbVideo.get(DbNames.Fields._id.name())).toString(); @@ -81,14 +85,13 @@ public class MediaDBImpl implements MediaDB { Date startTime = (Date) dbObject.get(DbNames.Fields.STARTTIME.name()); Integer durationInMillis = (Integer) dbObject.get(DbNames.Fields.DURATION_IN_MILLIS.name()); String mimeType = (String) dbObject.get(DbNames.Fields.MIME_TYPE.name()); - String mimeSubType = (String) dbObject.get(DbNames.Fields.MIME_SUBTYPE.name()); - DBMediaTrack dbMediaTrack = new DBMediaTrack(dbId, title, url, startTime, durationInMillis == null ? 0 : durationInMillis, mimeType, mimeSubType); + DBMediaTrack dbMediaTrack = new DBMediaTrack(dbId, title, url, startTime, durationInMillis == null ? 0 : durationInMillis, mimeType); return dbMediaTrack; } @Override public List loadAllMediaTracks() { - DBCursor cursor = getVideoCollection().find(); + DBCursor cursor = getVideoCollection().find().sort(sortByStartTimeAndTitle); List result = new ArrayList<>(cursor.count()); while (cursor.hasNext()) { result.add(createMediaObjectFromDB(cursor.next())); @@ -159,8 +162,9 @@ public class MediaDBImpl implements MediaDB { // Instead do the remaining filtering on client side... endTimeCondition.put(DbNames.Fields.STARTTIME.name(), new BasicDBObject("$lt", rangeStart)); - DBObject query = QueryBuilder.start().or(startTimeCondition, endTimeCondition).get(); - DBCursor cursor = getVideoCollection().find(query); + DBObject query = QueryBuilder.start().or(startTimeCondition, endTimeCondition).get(); + + DBCursor cursor = getVideoCollection().find(query).sort(sortByStartTimeAndTitle); List result = new ArrayList<>(cursor.count()); while (cursor.hasNext()) { diff --git a/java/com.sap.sailing.gwt.ui/.settings/com.google.gwt.eclipse.core.prefs b/java/com.sap.sailing.gwt.ui/.settings/com.google.gwt.eclipse.core.prefs index ee4cd4254c9..d68f978d311 100755 --- a/java/com.sap.sailing.gwt.ui/.settings/com.google.gwt.eclipse.core.prefs +++ b/java/com.sap.sailing.gwt.ui/.settings/com.google.gwt.eclipse.core.prefs @@ -1,5 +1,4 @@ -#Thu Feb 02 18:41:06 CET 2012 eclipse.preferences.version=1 entryPointModules= filesCopiedToWebInfLib=gwt-servlet.jar -gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDMgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0VdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDEwMjRtXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5BZG1pbkNvbnNvbGU8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuTGVhZGVyYm9hcmQ8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuTGVhZGVyYm9hcmRFZGl0aW5nPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLlJhY2VCb2FyZDwvZW50cnktcG9pbnQtbW9kdWxlPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5TcGVjdGF0b3I8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuVXNlck1hbmFnZW1lbnQ8L2VudHJ5LXBvaW50LW1vZHVsZT48L2d3dC1jb21waWxlLXNldHRpbmdzPg\=\= +gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDMgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0VdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDEwMjRtXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5BZG1pbkNvbnNvbGU8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuTGVhZGVyYm9hcmQ8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuTGVhZGVyYm9hcmRFZGl0aW5nPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLlJhY2VCb2FyZDwvZW50cnktcG9pbnQtbW9kdWxlPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5TcGVjdGF0b3I8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuVXNlck1hbmFnZW1lbnQ8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuVmlkZW9Qb3B1cDwvZW50cnktcG9pbnQtbW9kdWxlPjwvZ3d0LWNvbXBpbGUtc2V0dGluZ3M+ diff --git a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF index 381bccf4e6f..b5179b2f428 100755 --- a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF @@ -25,12 +25,13 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sailing.domain.swisstimingreplayadapter Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator Bundle-ActivationPolicy: lazy -Export-Package: com.sap.sailing.gwt.ui.client;x-friends:="com.sap.sailing.gwt.ui.test", - com.sap.sailing.gwt.ui.adminconsole;x-friends:="com.sap.sailing.gwt.ui.test", +Export-Package: com.sap.sailing.gwt.ui.adminconsole;x-friends:="com.sap.sailing.gwt.ui.test", + com.sap.sailing.gwt.ui.client;x-friends:="com.sap.sailing.gwt.ui.test", com.sap.sailing.gwt.ui.leaderboard;x-friends:="com.sap.sailing.gwt.ui.test", com.sap.sailing.gwt.ui.leaderboardedit;x-friends:="com.sap.sailing.gwt.ui.test", com.sap.sailing.gwt.ui.server;x-friends:="com.sap.sailing.gwt.ui.test", - com.sap.sailing.gwt.ui.shared;x-friends:="com.sap.sailing.gwt.ui.test" + com.sap.sailing.gwt.ui.shared;x-friends:="com.sap.sailing.gwt.ui.test", + com.sap.sailing.gwt.ui.shared.media Import-Package: javax.jms;version="1.1.0", javax.servlet;version="2.5.0", javax.servlet.http;version="2.5.0", diff --git a/java/com.sap.sailing.gwt.ui/VideoPopup.html b/java/com.sap.sailing.gwt.ui/VideoPopup.html new file mode 100644 index 00000000000..f580372704a --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/VideoPopup.html @@ -0,0 +1,13 @@ + + + + + SAP Sailing Video + + + + + + + + \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java index 74227326897..193a30f2237 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java @@ -80,7 +80,8 @@ public class AdminConsoleEntryPoint extends AbstractEntryPoint implements Regatt regattaDisplayers.add(leaderboardConfigPanel); tabPanel.add(new FregResultImportUrlPanel(sailingService, this, stringMessages), stringMessages.fregResultImportUrls(), /* asHTML */ false); tabPanel.add(new ReplicationPanel(sailingService, this, stringMessages), stringMessages.replication(), /* asHTML */ false); - tabPanel.add(new MediaPanel(mediaService, this, stringMessages), stringMessages.mediaPanel(), /* asHTML */ false); + final MediaPanel mediaPanel = new MediaPanel(mediaService, this, stringMessages); + tabPanel.add(mediaPanel, stringMessages.mediaPanel(), /* asHTML */ false); tabPanel.selectTab(0); tabPanel.addSelectionHandler(new SelectionHandler() { @@ -89,6 +90,9 @@ public class AdminConsoleEntryPoint extends AbstractEntryPoint implements Regatt if (leaderboardConfigPanel.isVisible()) { leaderboardConfigPanel.loadAndRefreshLeaderboards(); } + if (mediaPanel.isVisible()) { + mediaPanel.onShow(); + } } }); fillRegattas(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java index db33f2365ab..c5d5484789b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java @@ -206,7 +206,7 @@ public class MediaPanel extends FlowPanel { Column startTimeColumn = new Column(new EditTextCell()) { @Override public String getValue(MediaTrack mediaTrack) { - return mediaTrack.startTime == null ? "" : mediaTrack.startTimeToString(); + return mediaTrack.startTime == null ? "" : TimeFormatUtil.DATETIME_FORMAT.format(mediaTrack.startTime); } }; startTimeColumn.setSortable(true); @@ -330,4 +330,9 @@ public class MediaPanel extends FlowPanel { }); dialog.show(); } + + public void onShow() { + loadMediaTracks(); + } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java index 02e5f25d72b..76b152c98b4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaTrackDialog.java @@ -15,7 +15,7 @@ import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.ui.client.DataEntryDialog; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.media.MediaTrack; -import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MimeFileType; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MimeType; public class MediaTrackDialog extends DataEntryDialog { @@ -51,6 +51,8 @@ public class MediaTrackDialog extends DataEntryDialog { @Override protected MediaTrack getResult() { + mediaTrack.url = urlBox.getValue(); + mediaTrack.title = titleBox.getValue(); return mediaTrack; } @@ -125,22 +127,18 @@ public class MediaTrackDialog extends DataEntryDialog { int dotPos = lastPathSegment.lastIndexOf('.'); if (dotPos >= 0) { mediaTrack.title = lastPathSegment.substring(0, dotPos); - String fileEnding = lastPathSegment.substring(dotPos + 1); + String fileEnding = lastPathSegment.substring(dotPos + 1).toLowerCase(); try { - MimeFileType mimeFileType = MimeFileType.valueOf(fileEnding); - mediaTrack.type = mimeFileType.mediaType; - mediaTrack.subType = mimeFileType.mediaSubType; + mediaTrack.mimeType = MimeType.valueOf(fileEnding); } catch (IllegalArgumentException e) { // ignore. TODO: Somehow put it into the error message. // throw new IllegalArgumentException("Unsupported media type '" + mimeType + "'.", e); - mediaTrack.type = null; - mediaTrack.subType = null; + mediaTrack.mimeType = null; } } else { mediaTrack.title = mediaTrack.url; - mediaTrack.type = null; - mediaTrack.subType = null; + mediaTrack.mimeType = null; } refreshUI(); } @@ -148,7 +146,8 @@ public class MediaTrackDialog extends DataEntryDialog { private void refreshUI() { titleBox.setValue(mediaTrack.title, DONT_FIRE_EVENTS); mimeTypeLabel.setText(mediaTrack.typeToString()); - startTimeLabel.setText(mediaTrack.startTimeToString()); + String startTimeText = mediaTrack.startTime == null ? "undefined" : TimeFormatUtil.DATETIME_FORMAT.format(mediaTrack.startTime); + startTimeLabel.setText(startTimeText); durationLabel.setText(TimeFormatUtil.milliSecondsToHrsMinSec(mediaTrack.durationInMillis)); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaPlayer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaPlayer.java index a5c92d9fff9..6886390d0c9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaPlayer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaPlayer.java @@ -16,12 +16,16 @@ public interface MediaPlayer { MediaTrack getMediaTrack(); - void setMediaTrack(MediaTrack mediaTrack); - + double getDuration(); + + void setTime(double mediaTime); + + double getTime(); + void setPlaybackSpeed(double newPlaySpeedFactor); - boolean isMuted(); - void setMuted(boolean isToBeMuted); - + + void destroy(); + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractMediaPlayer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractMediaPlayer.java index a15e60d5d02..f8e1e0b2722 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractMediaPlayer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractMediaPlayer.java @@ -2,97 +2,37 @@ package com.sap.sailing.gwt.ui.raceboard; import java.util.Date; -import com.google.gwt.dom.client.MediaElement; -import com.google.gwt.media.client.MediaBase; import com.sap.sailing.gwt.ui.client.MediaPlayer; import com.sap.sailing.gwt.ui.shared.media.MediaTrack; public abstract class AbstractMediaPlayer implements MediaPlayer { private static final int TOLERATED_LAG_IN_MILLISECONDS = 2000; - protected final MediaBase mediaControl; - private MediaTrack mediaTrack; + private final MediaTrack mediaTrack; - public AbstractMediaPlayer(MediaBase mediaControl) { - this.mediaControl = mediaControl; - if (mediaControl != null) { - mediaControl.setControls(false); - mediaControl.setAutoplay(false); - mediaControl.setLoop(false); - mediaControl.setPreload(MediaElement.PRELOAD_AUTO); - } -} + protected AbstractMediaPlayer(MediaTrack mediaTrack) { + this.mediaTrack = mediaTrack; + } public MediaTrack getMediaTrack() { return mediaTrack; } - public void setMediaTrack(MediaTrack mediaTrack) { - this.mediaTrack = mediaTrack; - if (this.mediaTrack != null) { - mediaControl.setSrc(this.mediaTrack.url); - mediaControl.setTitle(mediaTrack.title); - } else { - mediaControl.setSrc(null); - mediaControl.setTitle(null); - } - } - public void alignTime(Date raceTime) { - switch (mediaControl.getReadyState()) { - case MediaElement.HAVE_NOTHING: - pause(); - break; - default: - long mediaStartTimeInMillis = mediaTrack.startTime.getTime(); - long mediaTimeInMillis = mediaStartTimeInMillis + Math.round(mediaControl.getCurrentTime() * 1000); - long raceTimeInMillis = raceTime.getTime(); - long mediaLaggingBehindRaceInMillis = raceTimeInMillis - mediaTimeInMillis; - if (Math.abs(mediaLaggingBehindRaceInMillis) > TOLERATED_LAG_IN_MILLISECONDS) { - double mediaTime = (raceTimeInMillis - mediaStartTimeInMillis) / 1000; - if (mediaTime < 0) { - pause(); - } else if (mediaTime > mediaControl.getDuration()) { - pause(); - } else { - mediaControl.setCurrentTime(mediaTime); - } + long mediaStartTimeInMillis = mediaTrack.startTime.getTime(); + long mediaTimeInMillis = mediaStartTimeInMillis + Math.round(getTime() * 1000); + long raceTimeInMillis = raceTime.getTime(); + long mediaLaggingBehindRaceInMillis = raceTimeInMillis - mediaTimeInMillis; + if (Math.abs(mediaLaggingBehindRaceInMillis) > TOLERATED_LAG_IN_MILLISECONDS) { + double mediaTime = (raceTimeInMillis - mediaStartTimeInMillis) / 1000; + if (mediaTime < 0) { + pause(); + } else if (mediaTime > getDuration()) { + pause(); + } else { + setTime(mediaTime); } } } - @Override - public boolean isPaused() { - return (mediaControl == null) || mediaControl.isPaused(); - } - - @Override - public void pause() { - if (mediaControl != null) { - mediaControl.pause(); - } - } - - @Override - public void play() { - if (mediaControl != null) { - mediaControl.play(); - } - } - - @Override - public void setPlaybackSpeed(double playbackSpeed) { - mediaControl.setPlaybackRate(playbackSpeed); - } - - @Override - public boolean isMuted() { - return mediaControl.isMuted(); - } - - @Override - public void setMuted(boolean isToBeMuted) { - mediaControl.setMuted(isToBeMuted); - } - } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AudioControl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AudioControl.java index 07e11220f77..9517a970550 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AudioControl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AudioControl.java @@ -1,11 +1,132 @@ package com.sap.sailing.gwt.ui.raceboard; +import com.google.gwt.dom.client.AudioElement; +import com.google.gwt.dom.client.MediaElement; import com.google.gwt.media.client.Audio; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack; public class AudioControl extends AbstractMediaPlayer { + + /** + * isReady and the ..Cache fields are required because setting the audio element's play state + * needs to be deferred until the media's metadata has been loaded. + */ + private boolean isReady = false; + private double deferredMediaTime; + private double deferredPlaybackSpeed = 1; + private boolean isDeferredPlaying; + private boolean isDeferredMuted; + + private final Audio audio; - public AudioControl() { - super(Audio.createIfSupported()); + public AudioControl(MediaTrack mediaTrack) { + super(mediaTrack); + audio = Audio.createIfSupported(); + if (audio != null) { + audio.setControls(false); + audio.setAutoplay(false); + audio.setLoop(false); + audio.setMuted(false); + audio.setPreload(MediaElement.PRELOAD_AUTO); + addNativeEventHandlers(audio.getAudioElement()); + audio.setSrc(mediaTrack.url); + audio.setTitle(mediaTrack.title); + } + } + + native void addNativeEventHandlers(AudioElement audioElement) /*-{ + var that = this; + audioElement.addEventListener('canplay', + function() { + that.@com.sap.sailing.gwt.ui.raceboard.AudioControl::initPlayState()(); + }); + }-*/; + + private void initPlayState() { + if (!isReady && (audio != null)) { + audio.setCurrentTime(deferredMediaTime); + audio.setMuted(isDeferredMuted); + audio.setPlaybackRate(deferredPlaybackSpeed); + if (isDeferredPlaying) { + audio.play(); + } else { + audio.pause(); + } + } + isReady = true; + } + + @Override + public boolean isPaused() { + if (isReady) { + return audio.isPaused(); + } else { + return !isDeferredPlaying; + } + } + + @Override + public void pause() { + isDeferredPlaying = false; + if (isReady && audio != null) { + audio.pause(); + } + } + + @Override + public void play() { + isDeferredPlaying = true; + if (isReady && audio != null) { + audio.play(); + } + } + + @Override + public void setPlaybackSpeed(double playbackSpeed) { + this.deferredPlaybackSpeed = playbackSpeed; + if (isReady && audio != null) { + audio.setPlaybackRate(playbackSpeed); + } + } + + @Override + public void setMuted(boolean isToBeMuted) { + isDeferredMuted = isToBeMuted; + if (isReady && audio != null) { + audio.setMuted(isToBeMuted); + } + } + + @Override + public double getDuration() { + if (isReady) { + return audio.getDuration(); + } else { + return Double.NaN; + } + } + + @Override + public double getTime() { + if (isReady) { + return audio.getCurrentTime(); + } else { + return Double.NaN; + } + } + + @Override + public void setTime(double mediaTime) { + this.deferredMediaTime = mediaTime; + if (isReady && audio != null) { + audio.setCurrentTime(mediaTime); + } + } + + @Override + public void destroy() { + audio.pause(); + audio.setSrc(null); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java index a25d1c64db1..c5ba531fa7c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelectionDialog.java @@ -1,6 +1,8 @@ package com.sap.sailing.gwt.ui.raceboard; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import com.google.gwt.event.logical.shared.CloseEvent; @@ -32,6 +34,7 @@ public class MediaSelectionDialog implements CloseHandler { private final DialogBox dialogControl; private final MediaSelectionListener mediaSelectionListener; + private final Map videoCheckBoxes = new HashMap(); MediaSelectionDialog(MediaSelectionListener mediaSelectionListener) { this.mediaSelectionListener = mediaSelectionListener; @@ -39,7 +42,7 @@ public class MediaSelectionDialog implements CloseHandler { dialogControl.setText("Select Playback Media"); dialogControl.addCloseHandler(this); } - + public void show(Collection videoTracks, Set selectedVideos, Collection audioTracks, MediaTrack selectedAudioTrack, UIObject popupLocation) { Panel grid = new VerticalPanel(); if (!audioTracks.isEmpty()) { @@ -52,18 +55,19 @@ public class MediaSelectionDialog implements CloseHandler { if (!videoTracks.isEmpty()) { grid.add(createVideoHeader()); for (MediaTrack videoTrack : videoTracks) { - grid.add(createVideoItem(videoTrack, selectedVideos)); + grid.add(createVideoCheckBox(videoTrack, selectedVideos)); } } dialogControl.add(grid); dialogControl.showRelativeTo(popupLocation); } - private Widget createVideoItem(final MediaTrack videoTrack, Set selectedVideos) { - CheckBox videoItem = new CheckBox(videoTrack.title); - videoItem.setTitle(videoTrack.toString()); - videoItem.setValue(selectedVideos.contains(videoTrack)); - videoItem.addValueChangeHandler(new ValueChangeHandler() { + private Widget createVideoCheckBox(final MediaTrack videoTrack, Set selectedVideos) { + CheckBox videoCheckBox = new CheckBox(videoTrack.title); + videoCheckBoxes.put(videoTrack, videoCheckBox); + videoCheckBox.setTitle(videoTrack.toString()); + videoCheckBox.setValue(selectedVideos.contains(videoTrack)); + videoCheckBox.addValueChangeHandler(new ValueChangeHandler() { @Override public void onValueChange(ValueChangeEvent changeEvent) { @@ -74,7 +78,15 @@ public class MediaSelectionDialog implements CloseHandler { } } }); - return videoItem; + + return videoCheckBox; + } + + public void unselectVideo(MediaTrack videoTrack) { + CheckBox videoCheckBox = videoCheckBoxes.get(videoTrack); + if (videoCheckBox != null) { + videoCheckBox.setValue(false); + } } private Widget createVideoHeader() { @@ -108,6 +120,7 @@ public class MediaSelectionDialog implements CloseHandler { @Override public void onClose(CloseEvent arg0) { dialogControl.clear(); + videoCheckBoxes.clear(); } public boolean isShowing() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java index 978ed3dbef8..2fd7da3dcb8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/MediaSelector.java @@ -7,11 +7,19 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import com.google.gwt.dom.client.AudioElement; +import com.google.gwt.dom.client.MediaElement; import com.google.gwt.dom.client.Style; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.logical.shared.CloseEvent; +import com.google.gwt.event.logical.shared.CloseHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.media.client.Audio; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.Window.ClosingEvent; +import com.google.gwt.user.client.Window.ClosingHandler; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.CheckBox; @@ -25,19 +33,21 @@ import com.sap.sailing.gwt.ui.client.TimeListener; import com.sap.sailing.gwt.ui.client.Timer.PlayModes; import com.sap.sailing.gwt.ui.client.Timer.PlayStates; import com.sap.sailing.gwt.ui.raceboard.MediaSelectionDialog.MediaSelectionListener; +import com.sap.sailing.gwt.ui.raceboard.VideoControl.VideoCloseListener; import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO; import com.sap.sailing.gwt.ui.shared.media.MediaTrack; public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateListener, TimeListener, - AsyncCallback>, MediaSelectionListener { + AsyncCallback>, MediaSelectionListener, CloseHandler, ClosingHandler { private final CheckBox toggleMediaButton; private final Button manageMediaButton; private final MediaSelectionDialog mediaSelectionDialog; + private final AudioElement mediaCanPlayTester; - private MediaPlayer audioPlayer; - private final Map videoPlayers = new HashMap(); + private MediaPlayer activeAudioPlayer; + private final Map videoPlayers = new HashMap(); private final Set audioTracks = new HashSet(); private final Set videoTracks = new HashSet(); private final ErrorReporter errorReporter; @@ -49,10 +59,12 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi public MediaSelector(ErrorReporter errorReporter) { this.errorReporter = errorReporter; + Window.addCloseHandler(this); + Window.addWindowClosingHandler(this); + mediaSelectionDialog = new MediaSelectionDialog(this); manageMediaButton = new Button(); - manageMediaButton.setEnabled(false); manageMediaButton.addClickHandler(new ClickHandler() { @Override @@ -60,7 +72,7 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi if (mediaSelectionDialog.isShowing()) { mediaSelectionDialog.hide(); } else { - MediaTrack playingAudioTrack = audioPlayer != null ? audioPlayer.getMediaTrack() : null; + MediaTrack playingAudioTrack = activeAudioPlayer != null ? activeAudioPlayer.getMediaTrack() : null; Set playingVideoTracks = videoPlayers.keySet(); mediaSelectionDialog.show(videoTracks, playingVideoTracks, audioTracks, playingAudioTrack, toggleMediaButton); } @@ -72,7 +84,6 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi toggleMediaButton = new CheckBox("Audio & Video"); - toggleMediaButton.setEnabled(false); toggleMediaButton.addStyleName("raceBoardNavigation-innerElement"); toggleMediaButton.getElement().getStyle().setFloat(Style.Float.LEFT); toggleMediaButton.addValueChangeHandler(new ValueChangeHandler() { @@ -89,7 +100,30 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi } }); + + setWidgetsVisible(false); + + Audio audio = Audio.createIfSupported(); + if (audio != null) { + mediaCanPlayTester = audio.getAudioElement(); + } else { + mediaCanPlayTester = null; + } } + + private boolean canPlay(MediaTrack mediaTrack) { + if (mediaCanPlayTester != null) { + String canPlay = mediaCanPlayTester.canPlayType(mediaTrack.typeToString()); + if (mediaCanPlayTester != null) { + return MediaElement.CAN_PLAY_PROBABLY.equals(canPlay) || MediaElement.CAN_PLAY_MAYBE.equals(canPlay); + } else { + return false; + } + } else { + return false; + } + } + private void playDefault() { MediaTrack defaultVideo = getDefaultVideo(); @@ -106,7 +140,6 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi if (!audioTracks.isEmpty()) { return audioTracks.iterator().next(); } else { - //TODO: determine best fitting audio, e.g. best overlap with race return getDefaultVideo(); } } @@ -115,22 +148,12 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi if (!videoTracks.isEmpty()) { return videoTracks.iterator().next(); } else { - //TODO: determine best fitting video, e.g. best overlap with race return null; } - } private void stopAll() { - if (audioPlayer != null) { - audioPlayer.pause(); - audioPlayer = null; - } - for (VideoPopup videoPlayer : videoPlayers.values()) { - videoPlayer.pause(); - videoPlayer.hide(); - } - videoPlayers.clear(); + clear(); } @@ -157,16 +180,17 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi @Override public void playSpeedFactorChanged(double newPlaySpeedFactor) { this.currentPlaybackSpeed = newPlaySpeedFactor; - if (audioPlayer != null) { - audioPlayer.setPlaybackSpeed(this.currentPlaybackSpeed); + if (activeAudioPlayer != null) { + activeAudioPlayer.setPlaybackSpeed(this.currentPlaybackSpeed); } - for (VideoPopup videoPlayer : videoPlayers.values()) { + for (VideoControl videoPlayer : videoPlayers.values()) { videoPlayer.setPlaybackSpeed(this.currentPlaybackSpeed); } } + private void pausePlaying() { - if (audioPlayer != null) { - audioPlayer.pause(); + if (activeAudioPlayer != null) { + activeAudioPlayer.pause(); } for (MediaPlayer player : videoPlayers.values()) { @@ -177,8 +201,8 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi } private void startPlaying() { - if (audioPlayer != null) { - audioPlayer.play(); + if (activeAudioPlayer != null) { + activeAudioPlayer.play(); } for (MediaPlayer player : videoPlayers.values()) { if (player.isPaused()) { @@ -190,8 +214,8 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi @Override public void timeChanged(Date raceTime) { this.currentRaceTime = raceTime; - if (audioPlayer != null) { - audioPlayer.alignTime(this.currentRaceTime); + if (activeAudioPlayer != null) { + activeAudioPlayer.alignTime(this.currentRaceTime); } for (MediaPlayer player : videoPlayers.values()) { player.alignTime(this.currentRaceTime); @@ -200,23 +224,30 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi @Override public void onFailure(Throwable caught) { + setWidgetsVisible(false); errorReporter - .reportError("Remote Procedure Call getPreviousConfigurations() - Failure: " + caught.getMessage()); + .reportError("Remote Procedure Call getMediaTracksForRace(...) - Failure: " + caught.getMessage()); } @Override public void onSuccess(Collection mediaTracks) { for (MediaTrack mediaTrack : mediaTracks) { - switch (mediaTrack.type) { - case video: - videoTracks.add(mediaTrack); - case audio: - audioTracks.add(mediaTrack); - break; + if (canPlay(mediaTrack)) { + switch (mediaTrack.mimeType.mediaType) { + case video: + videoTracks.add(mediaTrack); + case audio: //Intentional fall through. Video tracks are also considered audio tracks! + audioTracks.add(mediaTrack); + break; + } } } - manageMediaButton.setEnabled(mediaTracks.size() > 0); - toggleMediaButton.setEnabled(mediaTracks.size() > 0); + setWidgetsVisible(mediaTracks.size() > 0); + } + + private void setWidgetsVisible(boolean isVisible) { + manageMediaButton.setVisible(isVisible); + toggleMediaButton.setVisible(isVisible); } @Override @@ -227,25 +258,23 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi @Override public void audioChanged(MediaTrack audioTrack) { - if (audioPlayer != null) { + if (activeAudioPlayer != null) { - if (audioPlayer.getMediaTrack() == audioTrack) { + if (activeAudioPlayer.getMediaTrack() == audioTrack) { return; //nothing changed } - if (videoPlayers.containsKey(audioPlayer.getMediaTrack())) { //pre-change audioPlayer is one of the videoPlayers - audioPlayer.setMuted(true); - } else { - audioPlayer.pause(); - audioPlayer.setMediaTrack(null); + if (videoPlayers.containsKey(activeAudioPlayer.getMediaTrack())) { //pre-change audioPlayer is one of the videoPlayers + activeAudioPlayer.setMuted(true); + } else {//pre-change audioPlayer is a dedicated audio-only player + activeAudioPlayer.destroy(); } - audioPlayer = null; - + activeAudioPlayer = null; } - VideoPopup playingVideo = videoPlayers.get(audioTrack); + VideoControl playingVideo = videoPlayers.get(audioTrack); if (playingVideo != null) { - audioPlayer = playingVideo; - audioPlayer.setMuted(false); + activeAudioPlayer = playingVideo; + activeAudioPlayer.setMuted(false); } else { assignNewAudioPlayer(audioTrack); } @@ -255,22 +284,24 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi } @Override - public void videoSelected(MediaTrack videoTrack) { - VideoPopup playingVideo = videoPlayers.get(videoTrack); + public void videoSelected(final MediaTrack videoTrack) { + VideoControl playingVideo = videoPlayers.get(videoTrack); if (playingVideo == null) { - final VideoPopup videoPopup = new VideoPopup(); - if ((audioPlayer != null) && (audioPlayer.getMediaTrack() == videoTrack)) { //selected video track has been playing as audio-only - audioPlayer.pause(); - audioPlayer.setMediaTrack(null); - audioPlayer = videoPopup; - videoPopup.setMuted(false); + final VideoControl videoControl = new VideoControl(videoTrack, new VideoCloseListener() { + @Override + public void onVideoClosed() { + videoDeselected(videoTrack); + } + }); + videoPlayers.put(videoTrack, videoControl); + if ((activeAudioPlayer != null) && (activeAudioPlayer.getMediaTrack() == videoTrack)) { //selected video track has been playing as audio-only + activeAudioPlayer.pause(); + activeAudioPlayer = videoControl; + videoControl.setMuted(false); } else { - videoPopup.setMuted(true); + videoControl.setMuted(true); } - videoPopup.setMediaTrack(videoTrack); - synchPlayState(videoPopup); - videoPopup.show(); - videoPlayers.put(videoTrack, videoPopup); + synchPlayState(videoControl); } else { //nothing changed } @@ -281,28 +312,28 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi @Override public void videoDeselected(MediaTrack videoTrack) { - VideoPopup removedVideoPlayer = videoPlayers.remove(videoTrack); - removedVideoPlayer.pause(); - if (removedVideoPlayer == audioPlayer) { //in case this video has been the sound source, replace the video player with a dedicated audio player + mediaSelectionDialog.unselectVideo(videoTrack); + VideoControl removedVideoPlayer = videoPlayers.remove(videoTrack); + removedVideoPlayer.destroy(); + if (removedVideoPlayer == activeAudioPlayer) { //in case this video has been the sound source, replace the video player with a dedicated audio player assignNewAudioPlayer(removedVideoPlayer.getMediaTrack()); } - removedVideoPlayer.hide(); updateToggleButton(); } private void updateToggleButton() { - toggleMediaButton.setValue((audioPlayer != null) || (!videoPlayers.isEmpty())); + toggleMediaButton.setValue((activeAudioPlayer != null) || (!videoPlayers.isEmpty())); } private void assignNewAudioPlayer(MediaTrack audioTrack) { if (audioTrack != null) { - audioPlayer = new AudioControl(); - audioPlayer.setMediaTrack(audioTrack); - synchPlayState(audioPlayer); + activeAudioPlayer = new AudioControl(audioTrack); + + synchPlayState(activeAudioPlayer); } else { - audioPlayer = null; + activeAudioPlayer = null; } } @@ -326,4 +357,27 @@ public class MediaSelector implements RaceTimesInfoProviderListener, PlayStateLi return new Widget[] {toggleMediaButton, manageMediaButton}; } + @Override + public void onClose(CloseEvent arg0) { + clear(); + } + + @Override + public void onWindowClosing(ClosingEvent arg0) { + clear(); + } + + private void clear() { + if (activeAudioPlayer != null) { + videoPlayers.remove(activeAudioPlayer); //just to ensure that a potentially audio-playing video player is not destroyed a second time in the following video loop. + activeAudioPlayer.destroy(); + activeAudioPlayer = null; + } + for (VideoControl videoControl : videoPlayers.values()) { + videoControl.destroy(); + } + videoPlayers.clear(); + updateToggleButton(); + } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoControl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoControl.java new file mode 100644 index 00000000000..061d44963d7 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoControl.java @@ -0,0 +1,150 @@ +package com.sap.sailing.gwt.ui.raceboard; + +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.http.client.URL; +import com.google.gwt.user.client.Window; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack; + +public class VideoControl extends AbstractMediaPlayer { + + interface VideoCloseListener { + void onVideoClosed(); + } + + private final JavaScriptObject playerWindow; + private final VideoCloseListener videoCloseListener; + + public VideoControl(MediaTrack mediaTrack, VideoCloseListener videoCloseListener) { + super(mediaTrack); + this.videoCloseListener = videoCloseListener; + String videoUrl = getMediaTrack().url; + String title = getMediaTrack().title; + + String videoPlayerUrl = "/gwt/VideoPopup.html?url=" + URL.encodeQueryString(videoUrl) + "&title=" + URL.encodeQueryString(title); + + String codesvr = Window.Location.getParameter("gwt.codesvr"); + if (codesvr != null) { + videoPlayerUrl = videoPlayerUrl + "&gwt.codesvr=" + codesvr; + } + + playerWindow = openWindow(videoPlayerUrl); + registerNativeStuff(); + } + + native JavaScriptObject openWindow(String url) /*-{ + return $wnd.open(url, '_blank', "resizable=yes, scrollbars=no, toolbar=no, menubar=no, status=no, location=no, directories=no, personalbar=no, width=340, height=200"); + }-*/; + + @Override + public native void destroy() /*-{ + var playerWindow = this.@com.sap.sailing.gwt.ui.raceboard.VideoControl::playerWindow; + if (!playerWindow.closed) { + playerWindow.close(); + } + }-*/; + + native JavaScriptObject registerNativeStuff() /*-{ + var that = this; + var window = that.@com.sap.sailing.gwt.ui.raceboard.VideoControl::playerWindow; + window.onbeforeunload = function() { + that.@com.sap.sailing.gwt.ui.raceboard.VideoControl::onClosingPopup()(); + } + window.parent.deferredPlayState = { + isDeferredPlaying: false, + isDeferredMuted: true, + deferredMediaTime: 0, + playbackSpeed: 1 + }; + }-*/; + + private void onClosingPopup() { + pause(); + if (videoCloseListener != null) { + videoCloseListener.onVideoClosed(); + } + } + + @Override + public native void play() /*-{ + var window = this.@com.sap.sailing.gwt.ui.raceboard.VideoControl::playerWindow; + if (!window.parent.videoPlayer) { + window.parent.deferredPlayState.isDeferredPlaying = true; + } else { + window.parent.videoPlayer.play(); + } + + }-*/; + + @Override + public native void pause() /*-{ + var window = this.@com.sap.sailing.gwt.ui.raceboard.VideoControl::playerWindow; + if (!window.parent.videoPlayer) { + window.parent.deferredPlayState.isDeferredPlaying = false; + } else { + window.parent.videoPlayer.pause(); + } + + }-*/; + + @Override + public native void setTime(double mediaTime) /*-{ + var window = this.@com.sap.sailing.gwt.ui.raceboard.VideoControl::playerWindow; + if (!window.parent.videoPlayer) { + window.parent.deferredPlayState.deferredMediaTime = mediaTime; + } else { + window.parent.videoPlayer.setTime(mediaTime); + } + + }-*/; + + @Override + public native void setMuted(boolean muted) /*-{ + var window = this.@com.sap.sailing.gwt.ui.raceboard.VideoControl::playerWindow; + if (!window.parent.videoPlayer) { + window.parent.deferredPlayState.isDeferredMuted = muted; + } else { + window.parent.videoPlayer.setMuted(muted); + } + }-*/; + + @Override + public native boolean isPaused() /*-{ + var window = this.@com.sap.sailing.gwt.ui.raceboard.VideoControl::playerWindow; + if (!window.parent.videoPlayer) { + return !window.parent.deferredPlayState.isDeferredPlaying; + } else { + return window.parent.videoPlayer.isPaused(); + } + }-*/; + + @Override + public native double getDuration() /*-{ + var window = this.@com.sap.sailing.gwt.ui.raceboard.VideoControl::playerWindow; + if (!window.parent.videoPlayer) { + return NaN; + } else { + return window.parent.videoPlayer.getDuration(); + } + }-*/; + + @Override + public native double getTime() /*-{ + var window = this.@com.sap.sailing.gwt.ui.raceboard.VideoControl::playerWindow; + if (!window.parent.videoPlayer) { + return window.parent.deferredPlayState.deferredMediaTime; + } else { + return window.parent.videoPlayer.getTime(); + } + }-*/; + + @Override + public native void setPlaybackSpeed(double playbackSpeed) /*-{ + var window = this.@com.sap.sailing.gwt.ui.raceboard.VideoControl::playerWindow; + if (!window.parent.videoPlayer) { + window.parent.deferredPlayState.deferredPlaybackSpeed = playbackSpeed; + } else { + window.parent.videoPlayer.setPlaybackSpeed(playbackSpeed); + } + }-*/; + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java deleted file mode 100644 index d2a319e4805..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/VideoPopup.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.sap.sailing.gwt.ui.raceboard; - -import com.google.gwt.media.client.Video; -import com.google.gwt.user.client.ui.DialogBox; -import com.sap.sailing.gwt.ui.shared.media.MediaTrack; - -public class VideoPopup extends AbstractMediaPlayer { - - private final DialogBox dialogBox; - - public VideoPopup() { - super(Video.createIfSupported()); - this.dialogBox = new DialogBox(false, false); - if (mediaControl != null) { - - // HTML videoFrame = new - // HTML(""); - // - // SimplePanel videoFrameHolder = new SimplePanel(); - // videoFrameHolder.add(videoFrame); - - dialogBox.setWidget(mediaControl); - } - } - - public void show() { - dialogBox.show(); - } - - public void hide() { - dialogBox.hide(); - } - - @Override - public void setMediaTrack(MediaTrack videoTrack) { - super.setMediaTrack(videoTrack); - dialogBox.setText(videoTrack.title); - dialogBox.setTitle(videoTrack.toString()); - } - -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java index 0cdf1017daf..85adf4d784b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java @@ -21,6 +21,7 @@ import com.sap.sailing.gwt.ui.client.MediaService; import com.sap.sailing.gwt.ui.shared.media.MediaTrack; import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaSubType; import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaType; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MimeType; import com.sap.sailing.server.RacingEventService; public class MediaServiceImpl extends RemoteServiceServlet implements MediaService { @@ -45,8 +46,8 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi public Collection getMediaTracksForRace(RegattaAndRaceIdentifier regattaAndRaceIdentifier) { TrackedRace trackedRace = racingEventServiceTracker.getService().getExistingTrackedRace(regattaAndRaceIdentifier); if (trackedRace != null) { - Date raceStart = trackedRace.getStartOfTracking().asDate(); - Date raceEnd = trackedRace.getEndOfTracking().asDate(); + Date raceStart = trackedRace.getStartOfRace().asDate(); + Date raceEnd = trackedRace.getEndOfRace().asDate(); return createMediaTracksFromDB(mediaDB().queryOverlappingMediaTracks(raceStart , raceEnd)); } else { return Collections.emptyList(); @@ -81,17 +82,15 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi } private MediaTrack createMediaTrackFromDB(DBMediaTrack dbMediaTrack) { - MediaType mediaType = dbMediaTrack.mimeType != null ? MediaType.valueOf(dbMediaTrack.mimeType) : null; - MediaSubType mediaSubType = dbMediaTrack.mimeSubType != null ? MediaSubType.valueOf(dbMediaTrack.mimeSubType) : null; - MediaTrack mediaTrack = new MediaTrack(dbMediaTrack.dbId, dbMediaTrack.title, dbMediaTrack.url, dbMediaTrack.startTime, dbMediaTrack.durationInMillis, mediaType, mediaSubType); + MimeType mimeType = dbMediaTrack.mimeType != null ? MimeType.valueOf(dbMediaTrack.mimeType) : null; + MediaTrack mediaTrack = new MediaTrack(dbMediaTrack.dbId, dbMediaTrack.title, dbMediaTrack.url, dbMediaTrack.startTime, dbMediaTrack.durationInMillis, mimeType); return mediaTrack; } @Override public void addMediaTrack(MediaTrack mediaTrack) { - String mediaType = mediaTrack.type != null ? mediaTrack.type.name() : null; - String mediaSubType = mediaTrack.subType != null ? mediaTrack.subType.name() : null; - mediaDB().insertMediaTrack(mediaTrack.title, mediaTrack.url, mediaTrack.startTime, mediaTrack.durationInMillis, mediaType, mediaSubType); + String mimeType = mediaTrack.mimeType != null ? mediaTrack.mimeType.name() : null; + mediaDB().insertMediaTrack(mediaTrack.title, mediaTrack.url, mediaTrack.startTime, mediaTrack.durationInMillis, mimeType); } private MediaDB mediaDB() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java index e3006ef13ba..d2c30022b7e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaTrack.java @@ -3,7 +3,6 @@ package com.sap.sailing.gwt.ui.shared.media; import java.util.Date; import com.google.gwt.user.client.rpc.IsSerializable; -import com.sap.sailing.gwt.ui.adminconsole.TimeFormatUtil; /** * See http://my.opera.com/core/blog/2010/03/03/everything-you-need-to-know-about-html5-video-and-audio-2 @@ -12,14 +11,14 @@ import com.sap.sailing.gwt.ui.adminconsole.TimeFormatUtil; */ public class MediaTrack implements IsSerializable { - public enum MimeFileType { + public enum MimeType { - mp4(MediaType.video, MediaSubType.mpeg), ogv(MediaType.video, MediaSubType.ogg), qt(MediaType.video, MediaSubType.quicktime), mp3(MediaType.audio, MediaSubType.mpeg), ogg(MediaType.audio, MediaSubType.ogg), aac(MediaType.audio, MediaSubType.aac), webm(MediaType.video, MediaSubType.webm); + mp4(MediaType.video, MediaSubType.mp4), ogv(MediaType.video, MediaSubType.ogg), qt(MediaType.video, MediaSubType.quicktime), mp3(MediaType.audio, MediaSubType.mpeg), ogg(MediaType.audio, MediaSubType.ogg), aac(MediaType.audio, MediaSubType.aac), webm(MediaType.video, MediaSubType.webm); public final MediaType mediaType; public final MediaSubType mediaSubType; - MimeFileType(MediaType mediaType, MediaSubType mediaSubType) { + MimeType(MediaType mediaType, MediaSubType mediaSubType) { this.mediaType = mediaType; this.mediaSubType = mediaSubType; } @@ -35,7 +34,7 @@ public class MediaTrack implements IsSerializable { } public enum MediaSubType { - ogg, mpeg, x_aiff, quicktime, aac, webm; + ogg, mp4, mpeg, x_aiff, quicktime, aac, webm; public String toString() { return name().replace('_', '-'); @@ -55,41 +54,28 @@ public class MediaTrack implements IsSerializable { public String url; public Date startTime; public int durationInMillis; - public MediaType type; - public MediaSubType subType; + public MimeType mimeType; public MediaTrack() { super(); } - public MediaTrack(String title, String url, Date startTime, int durationInMillis, MediaType mediaType, MediaSubType mediaSubType) { - this(null, title, url, startTime, durationInMillis, mediaType, mediaSubType); - } - - public MediaTrack(String dbId, String title, String url, Date startTime, int durationInMillis, MediaType mediaType, - MediaSubType mediaSubType) { + public MediaTrack(String dbId, String title, String url, Date startTime, int durationInMillis, MimeType mimeType) { super(); this.dbId = dbId; this.title = title; this.url = url; this.startTime = startTime; this.durationInMillis = durationInMillis; - this.type = mediaType; - this.subType = mediaSubType; + this.mimeType = mimeType; } public String toString() { - return title + " - " + url + " [" + typeToString() + ']' + startTimeToString() + " [" + TimeFormatUtil.milliSecondsToHrsMinSec(durationInMillis) + ']'; + return title + " - " + url + " [" + typeToString() + ']' + startTime + " [" + durationInMillis + ']'; } public String typeToString() { - String typeString = type == null ? "undefined" : type.name(); - String subtypeString = subType == null ? "undefined" : subType.toString(); - return typeString + '/' + subtypeString ; - } - - public String startTimeToString() { - return startTime == null ? "undefined" : TimeFormatUtil.DATETIME_FORMAT.format(startTime); + return mimeType == null ? "undefined" : mimeType.toString(); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/video/VideoPopupWindow.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/video/VideoPopupWindow.java new file mode 100644 index 00000000000..fa94e4bb8d2 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/video/VideoPopupWindow.java @@ -0,0 +1,214 @@ +package com.sap.sailing.gwt.ui.video; + +import com.google.gwt.core.client.EntryPoint; +import com.google.gwt.dom.client.MediaElement; +import com.google.gwt.dom.client.VideoElement; +import com.google.gwt.event.dom.client.ContextMenuEvent; +import com.google.gwt.event.dom.client.ContextMenuHandler; +import com.google.gwt.event.logical.shared.ResizeEvent; +import com.google.gwt.event.logical.shared.ResizeHandler; +import com.google.gwt.media.client.Video; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.RootLayoutPanel; + +public class VideoPopupWindow implements EntryPoint, ResizeHandler, ContextMenuHandler { + + private Video video; + private boolean isDebug; + + @Override + public void onModuleLoad() { + + Window.addResizeHandler(this); + + RootLayoutPanel mainPanel = RootLayoutPanel.get(); + + String title = Window.Location.getParameter("title"); + Window.setTitle(title); + + String videoUrl = Window.Location.getParameter("url"); + + isDebug = Window.Location.getParameter("gwt.codesvr") != null; + + if (videoUrl != null) { + video = Video.createIfSupported(); + if (video != null) { + + addNativeEventHandlers(video.getVideoElement()); + video.addDomHandler(this, ContextMenuEvent.getType()); + + video.setPreload(MediaElement.PRELOAD_AUTO); + video.setMuted(true); + video.setAutoplay(false); + video.setControls(false); + video.setLoop(false); + video.setSrc(videoUrl); + mainPanel.add(video); + + } else { + mainPanel.add(new Label("Video not supported")); + } + } else { + mainPanel.add(new Label("Parameter 'url' not assigned.")); + } + + } + + native void addNativeEventHandlers(VideoElement videoElement) /*-{ + var that = this; + videoElement.addEventListener('loadedmetadata', function() { + that.@com.sap.sailing.gwt.ui.video.VideoPopupWindow::loadedmetadata()(); + }); + videoElement.addEventListener('canplay', function() { //see http://www.w3schools.com/tags/av_event_canplay.asp + var deferredPlayState = $wnd.deferredPlayState + if (deferredPlayState && !$wnd.videoPlayer) { + that.@com.sap.sailing.gwt.ui.video.VideoPopupWindow::initPlayState(DZDZ)(deferredPlayState.deferredMediaTime, deferredPlayState.isDeferredMuted, deferredPlayState.deferredPlaybackSpeed, deferredPlayState.isDeferredPlaying); + } + }); + }-*/; + + native void addCallbackMethods() /*-{ + var that = this; + $wnd.videoPlayer = { + play : function() { + that.@com.sap.sailing.gwt.ui.video.VideoPopupWindow::play()(); + }, + pause : function() { + that.@com.sap.sailing.gwt.ui.video.VideoPopupWindow::pause()(); + }, + setTime : function(time) { + that.@com.sap.sailing.gwt.ui.video.VideoPopupWindow::setTime(D)(time); + }, + setMuted : function(muted) { + that.@com.sap.sailing.gwt.ui.video.VideoPopupWindow::setMuted(Z)(muted); + }, + isPaused : function() { + return that.@com.sap.sailing.gwt.ui.video.VideoPopupWindow::isPaused()(); + }, + getDuration : function() { + return that.@com.sap.sailing.gwt.ui.video.VideoPopupWindow::getDuration()(); + }, + getTime : function() { + return that.@com.sap.sailing.gwt.ui.video.VideoPopupWindow::getTime()(); + }, + setPlaybackSpeed : function(newPlaySpeedFactor) { + that.@com.sap.sailing.gwt.ui.video.VideoPopupWindow::setPlaybackSpeed(D)(newPlaySpeedFactor); + } + }; + }-*/; + + public void play() { + video.play(); + } + + public void pause() { + video.pause(); + } + + public void setTime(double time) { + video.setCurrentTime(time); + } + + public void setMuted(boolean muted) { + video.setMuted(muted); + } + + public boolean isPaused() { + return video.isPaused(); + } + + public double getDuration() { + return video.getDuration(); + } + + public double getTime() { + return video.getCurrentTime(); + } + + public void setPlaybackSpeed(double newPlaySpeedFactor) { + video.setPlaybackRate(newPlaySpeedFactor); + } + + public void loadedmetadata() { + adjustWindowSize(); + } + + public void initPlayState(double deferredMediaTime, boolean isDeferredMuted, double deferredPlaybackSpeed, + boolean isDeferredPlaying) { + + addCallbackMethods(); + + setTime(deferredMediaTime); + setMuted(isDeferredMuted); + setPlaybackSpeed(deferredPlaybackSpeed); + if (isDeferredPlaying) { + play(); + } else { + pause(); + } + + } + + private void adjustWindowSize() { + int clientWidth = Window.getClientWidth(); + int videoWidth = video.getVideoWidth(); + int widthDelta = videoWidth - clientWidth; + + int clientHeight = Window.getClientHeight(); + int videoHeight = video.getVideoHeight(); + int heightDelta = videoHeight - clientHeight; + + Window.resizeBy(widthDelta, heightDelta); + } + + @Override + public void onResize(final ResizeEvent event) { + if (video != null) { + new Timer() { + @Override + public void run() { + int videoWidth = video.getVideoWidth(); + if (videoWidth > 0) { + int clientWidth = Window.getClientWidth(); + double widthResizeRatio = ((double) clientWidth) / videoWidth; + + int videoHeight = video.getVideoHeight(); + if (videoHeight > 0) { + int clientHeight = Window.getClientHeight(); + double heightResizeRatio = ((double) clientHeight) / videoHeight; + + double resizeRatio = Math.min(widthResizeRatio, heightResizeRatio); + + if (Math.abs(1.0 - resizeRatio) > 0.001) { + + int newVideoWidth = (int) Math.round(resizeRatio * videoWidth); + int newVideoHeight = (int) Math.round(resizeRatio * videoHeight); + video.setPixelSize(newVideoWidth, newVideoHeight); + + int widthDelta = newVideoWidth - clientWidth; + int heightDelta = newVideoHeight - clientHeight; + + Window.resizeBy(widthDelta, heightDelta); + } + } + } + + } + }.schedule(100); + } + } + + /** + * Suppressing context menu. + */ + @Override + public void onContextMenu(ContextMenuEvent event) { + if (!isDebug) { + event.preventDefault(); + event.stopPropagation(); + } + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/VideoPopup.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/VideoPopup.gwt.xml new file mode 100644 index 00000000000..41432f778b4 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/VideoPopup.gwt.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestMediaDB.java b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestMediaDB.java index da52fe3725f..001b37c3aee 100644 --- a/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestMediaDB.java +++ b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/TestMediaDB.java @@ -12,8 +12,7 @@ import com.mongodb.MongoException; import com.sap.sailing.domain.persistence.media.DBMediaTrack; import com.sap.sailing.domain.persistence.media.MediaDB; import com.sap.sailing.domain.persistence.media.MediaDBFactory; -import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaSubType; -import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MediaType; +import com.sap.sailing.gwt.ui.shared.media.MediaTrack.MimeType; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; @@ -39,9 +38,8 @@ public class TestMediaDB extends AbstractMongoDBTest { MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); Date date = new Date(); int durationInMillis = 23; - String mediaType = MediaType.video.name(); - String mediaSubType = MediaSubType.ogg.name(); - String dbId = mongoDB.insertMediaTrack(videoTitle, url, date, durationInMillis, mediaType, mediaSubType); + String mimeType = MimeType.ogv.name(); + String dbId = mongoDB.insertMediaTrack(videoTitle, url, date, durationInMillis, mimeType); DBMediaTrack videoTrack = mongoDB.loadMediaTrack(dbId); assertNotNull(videoTrack); assertThat(videoTrack.title, Is.is(videoTitle)); @@ -68,11 +66,10 @@ public class TestMediaDB extends AbstractMongoDBTest { final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; Date date = new Date(); int durationInMillis = 23; - String mediaType = MediaType.video.name(); - String mediaSubType = MediaSubType.ogg.name(); + String mimeType = MimeType.ogv.name(); for (int i = 0; i < count; i++) { - mongoDB.insertMediaTrack(videoTitleTemplate + i, url, date, durationInMillis, mediaType, mediaSubType); + mongoDB.insertMediaTrack(videoTitleTemplate + i, url, date, durationInMillis, mimeType); } } @@ -84,9 +81,8 @@ public class TestMediaDB extends AbstractMongoDBTest { MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); Date date = new Date(); int durationInMillis = 23; - String mediaType = MediaType.video.name(); - String mediaSubType = MediaSubType.ogg.name(); - String dbId = mongoDB.insertMediaTrack(videoTitle, url, date, durationInMillis, mediaType, mediaSubType); + String mimeType = MimeType.ogv.name(); + String dbId = mongoDB.insertMediaTrack(videoTitle, url, date, durationInMillis, mimeType); //delete mongoDB.deleteMediaTrack(videoTitle); @@ -103,9 +99,8 @@ public class TestMediaDB extends AbstractMongoDBTest { MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); Date originalDate = new Date(); int durationInMillis = 23; - String mediaType = MediaType.video.name(); - String mediaSubType = MediaSubType.ogg.name(); - String dbId = mongoDB.insertMediaTrack(videoTitle, url, originalDate, durationInMillis, mediaType, mediaSubType); + String mimeType = MimeType.ogv.name(); + String dbId = mongoDB.insertMediaTrack(videoTitle, url, originalDate, durationInMillis, mimeType); //update with new date Date newDate = new Date(originalDate.getTime() + 1000); @@ -117,8 +112,7 @@ public class TestMediaDB extends AbstractMongoDBTest { assertThat(videoTrack.durationInMillis, is(durationInMillis)); assertThat(videoTrack.title, is(videoTitle)); assertThat(videoTrack.url, is(url)); - assertThat(videoTrack.mimeType, is(mediaType)); - assertThat(videoTrack.mimeSubType, is(mediaSubType)); + assertThat(videoTrack.mimeType, is(mimeType)); } @@ -207,9 +201,8 @@ public class TestMediaDB extends AbstractMongoDBTest { final String videoTitle = "Test Video"; final String url = "test"; MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); - String mediaType = MediaType.video.name(); - String mediaSubType = MediaSubType.ogg.name(); - String dbId = mongoDB.insertMediaTrack(videoTitle, url, startTime, oneHourInMillis, mediaType, mediaSubType); + String mimeType = MimeType.ogv.name(); + String dbId = mongoDB.insertMediaTrack(videoTitle, url, startTime, oneHourInMillis, mimeType); Collection videoTracks = mongoDB.queryOverlappingMediaTracks(rangeStart, rangeEnd); assertThat(videoTracks.size(), is(1)); @@ -221,9 +214,8 @@ public class TestMediaDB extends AbstractMongoDBTest { final String videoTitle = "Test Video"; final String url = "test"; MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); - String mediaType = MediaType.video.name(); - String mediaSubType = MediaSubType.ogg.name(); - String dbId = mongoDB.insertMediaTrack(videoTitle, url, startTime, oneHourInMillis, mediaType, mediaSubType); + String mimeType = MimeType.ogv.name(); + mongoDB.insertMediaTrack(videoTitle, url, startTime, oneHourInMillis, mimeType); Collection videoTracks = mongoDB.queryOverlappingMediaTracks(rangeStart, rangeEnd); assertThat(videoTracks.size(), is(0)); diff --git a/java/com.sap.sailing.server/SailingServer (Proxy).launch b/java/com.sap.sailing.server/SailingServer (Proxy).launch index 7097ca3bb62..ce12c346b3b 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy).launch @@ -25,6 +25,6 @@ - + From e6b42f162c0121925dff4aa68d17f01f4d7f8b88 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Thu, 10 Jan 2013 00:14:36 +0100 Subject: [PATCH 11/31] Initial commit for embedded Youtube video. Just copied from test code, not been running yet. --- java/com.sap.sailing.gwt.ui/YoutubePopup.html | 13 ++++ .../gwt/ui/video/YoutubePopupWindow.java | 76 +++++++++++++++++++ .../sap/sailing/gwt/ui/YoutubePopup.gwt.xml | 8 ++ 3 files changed, 97 insertions(+) create mode 100644 java/com.sap.sailing.gwt.ui/YoutubePopup.html create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/video/YoutubePopupWindow.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/YoutubePopup.gwt.xml diff --git a/java/com.sap.sailing.gwt.ui/YoutubePopup.html b/java/com.sap.sailing.gwt.ui/YoutubePopup.html new file mode 100644 index 00000000000..0fb320c2159 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/YoutubePopup.html @@ -0,0 +1,13 @@ + + + + + SAP Sailing Video + + + + + + + + \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/video/YoutubePopupWindow.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/video/YoutubePopupWindow.java new file mode 100644 index 00000000000..aca5e64a22d --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/video/YoutubePopupWindow.java @@ -0,0 +1,76 @@ +package com.sap.sailing.gwt.ui.video; + +import com.google.gwt.core.client.EntryPoint; +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.RootLayoutPanel; +import com.google.gwt.user.client.ui.RootPanel; + +public class YoutubePopupWindow implements EntryPoint { + + private static final String VIDEO_CONTAINER_ID = "videoContainer"; + + private JavaScriptObject youtubePlayer; + + @Override + public void onModuleLoad() { + String title = Window.Location.getParameter("title"); + Window.setTitle(title); + + RootLayoutPanel.get().getElement().setId(VIDEO_CONTAINER_ID); + + String videoUrl = Window.Location.getParameter("id"); + if (videoUrl != null) { + loadYoutube(videoUrl, VIDEO_CONTAINER_ID); + } + } + + //inspired by https://developers.google.com/youtube/iframe_api_reference#Examples + private native void loadYoutube(String videoUrl, String videoContainerId) /*-{ + + var that = this; + + var tag = $doc.createElement('script'); + + // This is a protocol-relative URL as described here: + // http://paulirish.com/2010/the-protocol-relative-url/ + // If you're testing a local page accessed via a file:/// URL, please set tag.src to + // "https://www.youtube.com/iframe_api" instead. + tag.src = "//www.youtube.com/iframe_api"; + var firstScriptTag = $doc.getElementsByTagName('script')[0]; + firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); + + // This function creates an