/*******************************************************************************
* Copyright (c) 2012, 2016 EclipseSource and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
package com.eclipsesource.tabris.widgets;
import static com.eclipsesource.tabris.internal.Clauses.whenNull;
import static com.eclipsesource.tabris.internal.Constants.PROPERTY_CACHE_SIZE;
import static com.eclipsesource.tabris.internal.Constants.PROPERTY_PARENT;
import static com.eclipsesource.tabris.internal.Constants.PROPERTY_URL;
import static com.eclipsesource.tabris.internal.Constants.TYPE_VIDEO;
import static org.eclipse.rap.rwt.widgets.WidgetUtil.getId;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.remote.Connection;
import org.eclipse.rap.rwt.remote.RemoteObject;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import com.eclipsesource.tabris.internal.VideoOperationHandler;
import com.eclipsesource.tabris.internal.VideoRemoteAdapter;
/**
* <p>
* A <code>Video</code> can be used to play videos from an URL on a mobile
* device. It provides facilities to control the video from the server side and
* to receive notification when the client controls will be triggered.
* </p>
*
* @see PlaybackListener
* @since 0.7
*/
public class Video extends Composite {
/**
* <p>
* The current playback state of the video player. This may also change as a
* result of the user interacting with the video player controls.
* </p>
*
* @since 1.4.9
*/
public static enum PlayerState {
/**
* Player state is unknown. This is the initial state.
*/
UNKNOWN,
/**
* Player is ready to play.
*/
READY,
/**
* Video playback has stalled temporarily. It will resume once
* more data is loaded.
*/
STALLED,
/**
* Video playback has failed.
*/
FAILED,
/**
* Video playback has finished.
*/
FINISHED;
}
/**
* Speed for normal playback ({@value}).
*
* @see #setSpeed(float)
* @since 1.4.9
*/
public static final float PLAY_SPEED = 1.0f;
/**
* Speed for pausing the playback ({@value}).
*
* @see #setSpeed(float)
* @since 1.4.9
*/
public static final float PAUSE_SPEED = 0.0f;
private final List<PlaybackListener> playbackListeners = new ArrayList<PlaybackListener>();
private int cacheSizeMb;
private URL videoUrl;
private float speed = PAUSE_SPEED;
private boolean controlsVisible;
private final RemoteObject remoteObject;
private final VideoRemoteAdapter remoteAdapter;
/**
* Creates a new Video object.
*
* @param parent the parent Composite; never null
* @param videoUrl a String with the url of the video to play; never null
* @throws IllegalArgumentException when the passed URL string is not a valid
* url.
* @since 1.4.9
*/
public Video( Composite parent, String videoUrl ) throws IllegalArgumentException {
this( parent, videoUrl, 0 );
}
/**
* Creates a new Video object.
*
* @param videoUrl a String with the url of the video to play; never null
* @param cacheSizeMb the maximum allowed size of the client-side video cache
* in megabytes (0 or greater).
* @param parent the parent Composite; never null
* @throws IllegalArgumentException when the passed URl string is not a valid
* url.
* @since 1.4.9
*/
public Video( Composite parent, String videoUrl, int cacheSizeMb )
throws IllegalArgumentException
{
super( parent, SWT.NONE );
setCacheSize( cacheSizeMb );
setVideoUrl( videoUrl );
Connection connection = RWT.getUISession().getConnection();
remoteObject = connection.createRemoteObject( TYPE_VIDEO );
remoteObject.setHandler( new VideoOperationHandler( this ) );
remoteObject.set( PROPERTY_PARENT, getId( this ) );
remoteObject.set( PROPERTY_CACHE_SIZE, getCacheSize() );
remoteObject.set( PROPERTY_URL, getURL().toString() );
remoteAdapter = new VideoRemoteAdapter( this, remoteObject );
}
private void setCacheSize( int cacheSizeMb ) {
if( cacheSizeMb < 0 ) {
throw new IllegalArgumentException( String.valueOf( cacheSizeMb ) );
}
this.cacheSizeMb = cacheSizeMb;
}
private void setVideoUrl( String videoUrl ) {
try {
this.videoUrl = new URL( videoUrl );
} catch( MalformedURLException mfURLe ) {
throw new IllegalArgumentException( videoUrl + " is not a valid url", mfURLe );
}
}
/**
* Instructs the device to remove all cached content from the video cache.
* <p>
* The call is asynchronous, the files are <b>not</b> removed immediately upon
* returning.
*
* @since 1.4.9
*/
public void clearCache() {
remoteAdapter.setClearCache( true );
}
/**
* Returns the maximum allowed video cache size in megabytes. The default is
* 0.
*
* @return cache size in megabytes (0 or greater).
* @since 1.4.9
*/
public int getCacheSize() {
return cacheSizeMb;
}
/**
* Returns the url of the video to play.
*/
public URL getURL() {
return videoUrl;
}
/**
* Returns the current speed of the video playback.
*
* @see #PLAY_SPEED
* @see #PAUSE_SPEED
* @since 1.4.9
*/
public float getSpeed() {
return speed;
}
/**
* Set the playback speed. A positive value of 1.0 plays forward at normal speed.
* A negative value plays backward. A value of 0.0 pauses.
*
* @see #PLAY_SPEED
* @see #PAUSE_SPEED
* @since 1.4.9
*/
public void setSpeed( float speed ) {
if( this.speed != speed ) {
remoteAdapter.preservePlaybackSpeed( this.speed );
this.speed = speed;
}
}
/**
* Returns true, if video controls are visible on the client.
*/
public boolean hasPlayerControlsVisible() {
return controlsVisible;
}
/**
* Controls if video controls should be visible on the client or not.
*/
public void setPlayerControlsVisible( boolean controlsVisible ) {
if( this.controlsVisible != controlsVisible ) {
remoteAdapter.preserveControlsVisible( this.controlsVisible );
this.controlsVisible = controlsVisible;
}
}
/**
* Adds a {@link PlaybackListener} to receive notifications of video events
* like play or stop.
*
* @since 1.0
*/
public void addPlaybackListener( PlaybackListener listener ) {
whenNull( listener ).throwIllegalArgument( "PlaybackListener must not be null" );
remoteAdapter.preservePlaybackListener( hasPlaybackListener() );
playbackListeners.add( listener );
}
/**
* Removes a {@link PlaybackListener}.
*
* @since 1.0
*/
public void removePlaybackListener( PlaybackListener listener ) {
whenNull( listener ).throwIllegalArgument( "PlaybackListener must not be null" );
remoteAdapter.preservePlaybackListener( hasPlaybackListener() );
playbackListeners.remove( listener );
}
/**
* <p>
* Returns the added {@link PlaybackListener}s
* </p>
*
* @since 1.4.9
*/
public List<PlaybackListener> getPlaybackListeners() {
return new ArrayList<PlaybackListener>( playbackListeners );
}
/**
* Steps to a specific point in time of the video to play.
*
* @param seconds Seconds before decimal point; milliseconds after decimal
* point. If the argument is too large, the play position will be
* moved to the end of the video. If the argument is too small, the
* play position will be moved to the start of the video.
* @since 1.4.9
*/
public void stepToTime( float seconds ) {
remoteAdapter.setStepToTime( seconds );
}
/**
* Skip forward or backward from the current playback position. Will be
* affected by latency.
*
* @param seconds Seconds before the decimal point; milliseconds after the
* decimal point. A positive value skips forward; a negative value
* skips backward. If the argument is too large, the play position
* will be moved to the end of the video. If the argument is too
* small, the play position will be moved to the start of the video.
* @since 1.4.9
*/
public void skipFromCurrent( float seconds ) {
remoteAdapter.setSkipFromCurrent( seconds );
}
@Override
public void setBounds( Rectangle bounds ) {
remoteAdapter.preserveBounds( getBounds() );
super.setBounds( bounds );
}
private boolean hasPlaybackListener() {
return !playbackListeners.isEmpty();
}
}