gwt.material.design.addins.client.camera.MaterialCameraCapture.java Source code

Java tutorial

Introduction

Here is the source code for gwt.material.design.addins.client.camera.MaterialCameraCapture.java

Source

package gwt.material.design.addins.client.camera;

/*
 * #%L
 * GwtMaterial
 * %%
 * Copyright (C) 2015 - 2017 GwtMaterialDesign
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.*;
import com.google.gwt.event.shared.HandlerRegistration;
import gwt.material.design.addins.client.camera.base.HasCameraActions;
import gwt.material.design.addins.client.camera.base.HasCameraCaptureHandlers;
import gwt.material.design.addins.client.camera.constants.CameraFacingMode;
import gwt.material.design.addins.client.camera.events.CameraCaptureEvent;
import gwt.material.design.addins.client.camera.events.CameraCaptureEvent.CaptureStatus;
import gwt.material.design.addins.client.camera.events.CameraCaptureHandler;
import gwt.material.design.client.base.JsLoader;
import gwt.material.design.client.base.MaterialWidget;
import gwt.material.design.jscore.client.api.Navigator;
import gwt.material.design.jscore.client.api.media.*;

import static gwt.material.design.addins.client.camera.JsCamera.$;

//@formatter:off

/**
 * <p>
 * MaterialCameraCapture is a widget that captures the video stream from the camera, using the
 * HTML5 {@code MediaDevices.getUserMedia()} (Streams API). This widget can capture images from the video, to allow
 * the upload to the server of photos from the camera.
 * </p>
 * <p>
 * <h3>XML Namespace Declaration</h3>
 * <pre>
 * {@code
 * xmlns:ma='urn:import:gwt.material.design.addins.client'
 * }
 * </pre>
 * <p>
 * <h3>UiBinder Usage:</h3>
 * <p><pre>
 * {@code
 * <ma:camera.MaterialCameraCapture ui:field="camera" />
 * }
 * </pre></p>
 * <p>
 * <h3>Java Usage:</h3>
 * <p><pre>
 *  if (MaterialCameraCapture.isSupported()){
 *      MaterialCameraCapture camera = new MaterialCameraCapture();
 *      camera.addCameraCaptureHandler(new CameraCaptureHandler() {
 *          {@literal @}Override
 *          public void onCameraCaptureChange(CameraCaptureEvent event) {
 *              if (event.getCaptureStatus() == CaptureStatus.ERRORED){
 *                  Window.alert("Error on starting the camera capture: " + event.getErrorMessage());
 *                  ((MaterialCameraCapture)event.getSource()).removeFromParent();
 *              }
 *          }
 *      });
 *      add(camera); //adds to the layout
 *  }
 *  else {
 *      Window.alert("Sorry, your browser doesn't support the camera capture.");
 *  }
 * </pre></p>
 * <p>
 * <h3>Styling:</h3>
 * <p>
 * To limit the width of the camera capture widget on mobile devices, you can use {@code max-width: 100%} on the widget.
 * The browser will take care of the aspect ratio of the video.
 * </p>
 * <p>
 * <h3>Notice:</h3>
 * <p>
 * This widget only works on pages served by a secure protocol (HTTPS). For the browser compatibility,
 * access <a href="http://caniuse.com/#feat=stream">http://caniuse.com/#feat=stream</a>
 * </p>
 *
 * @author gilberto-torrezan
 * @author kevzlou7979
 */
// @formatter:on
public class MaterialCameraCapture extends MaterialWidget
        implements JsLoader, HasCameraCaptureHandlers, HasCameraActions {

    protected int width = 1280;
    protected int height = 720;
    protected boolean pauseOnUnload = false;
    protected CameraFacingMode facingMode = CameraFacingMode.FRONT;
    private MediaStream mediaStream;
    private MaterialWidget video = new MaterialWidget(Document.get().createVideoElement());
    private MaterialWidget overlayPanel = new MaterialWidget(Document.get().createDivElement());

    public MaterialCameraCapture() {
        super(Document.get().createDivElement(), "camera-wrapper");
    }

    @Override
    protected void onLoad() {
        super.onLoad();

        setLayoutPosition(Style.Position.RELATIVE);
        add(video);

        overlayPanel.setLayoutPosition(Style.Position.FIXED);
        overlayPanel.setTop(0);
        overlayPanel.setLeft(0);
        overlayPanel.setBottom(0);
        overlayPanel.setRight(0);
        add(overlayPanel);

        load();
    }

    @Override
    public void load() {
        play();
    }

    @Override
    protected void onUnload() {
        super.onUnload();

        unload();
    }

    @Override
    public void unload() {
        stop();
    }

    @Override
    public void reload() {
        unload();
        load();
    }

    @Override
    public void play() {
        if (!isSupported()) {
            onCameraCaptureError("MaterialCameraCapture is not supported in this browser.");
            return;
        }

        nativePlay(video.getElement());
    }

    @Override
    public void pause() {
        VideoElement el = video.getElement().cast();
        el.pause();
        onCameraCapturePause();
    }

    @Override
    public void stop() {
        if (pauseOnUnload) {
            pause();
        }

        if (mediaStream != null) {
            for (MediaStreamTrack track : mediaStream.getTracks()) {
                track.stop();
            }
        }
    }

    @Override
    public String captureToDataURL() {
        return captureToDataURL("image/png");
    }

    @Override
    public String captureToDataURL(String mimeType) {
        return nativeCaptureToDataURL(Canvas.createIfSupported().getCanvasElement(), video.getElement(), mimeType);
    }

    /**
     * Sets if the camera capture should pause when the widget is unloaded.
     * The default is <code>true</code>.
     */
    public void setPauseOnUnload(boolean pauseOnUnload) {
        this.pauseOnUnload = pauseOnUnload;
    }

    /**
     * Returns if the camera capture should pause when the widget is unloaded.
     * The default is <code>true</code>.
     */
    public boolean isPauseOnUnload() {
        return pauseOnUnload;
    }

    /**
     * Native call to the streams API
     */
    protected void nativePlay(Element video) {
        MediaStream stream = null;
        if (Navigator.getUserMedia != null) {
            stream = Navigator.getUserMedia;
            GWT.log("Uses Default user Media");
        } else if (Navigator.webkitGetUserMedia != null) {
            stream = Navigator.webkitGetUserMedia;
            GWT.log("Uses Webkit User Media");
        } else if (Navigator.mozGetUserMedia != null) {
            stream = Navigator.mozGetUserMedia;
            GWT.log("Uses Moz User Media");
        } else if (Navigator.msGetUserMedia != null) {
            stream = Navigator.msGetUserMedia;
            GWT.log("Uses Microsoft user Media");
        } else {
            GWT.log("No supported media found in your browser");
        }

        if (stream != null) {
            Navigator.getMedia = stream;
            Constraints constraints = new Constraints();
            constraints.audio = false;

            MediaTrackConstraints mediaTrackConstraints = new MediaTrackConstraints();
            mediaTrackConstraints.width = width;
            mediaTrackConstraints.height = height;
            mediaTrackConstraints.facingMode = facingMode.getName();
            constraints.video = mediaTrackConstraints;

            Navigator.mediaDevices.getUserMedia(constraints).then(streamObj -> {
                mediaStream = (MediaStream) streamObj;
                if (URL.createObjectURL(mediaStream) != null) {
                    $(video).attr("src", URL.createObjectURL(mediaStream));
                } else if (WebkitURL.createObjectURL(mediaStream) != null) {
                    $(video).attr("src", WebkitURL.createObjectURL(mediaStream));
                }
                if (video instanceof VideoElement) {
                    ((VideoElement) video).play();
                }
                onCameraCaptureLoad();
                return null;
            }).catchException(error -> {
                GWT.log("MaterialCameraCapture: An error occured! " + error);
                onCameraCaptureError(error.toString());
                return null;
            });
        }
    }

    /**
     * Native call to capture the frame of the video stream.
     */
    protected String nativeCaptureToDataURL(CanvasElement canvas, Element element, String mimeType) {
        VideoElement videoElement = (VideoElement) element;
        int width = videoElement.getVideoWidth();
        int height = videoElement.getVideoHeight();
        if (Double.isNaN(width) || Double.isNaN(height)) {
            width = videoElement.getClientWidth();
            height = videoElement.getClientHeight();
        }
        canvas.setWidth(width);
        canvas.setHeight(height);
        Context2d context = canvas.getContext2d();
        context.drawImage(videoElement, 0, 0, width, height);
        return canvas.toDataUrl(mimeType);
    }

    /**
     * Tests if the browser supports the Streams API. This should be called before creating any
     * MaterialCameraCapture widgets to avoid errors on the browser.
     *
     * @return <code>true</code> if the browser supports this widget, <code>false</code> otherwise
     */
    public static boolean isSupported() {
        return Navigator.webkitGetUserMedia != null || Navigator.getUserMedia != null
                || Navigator.mozGetUserMedia != null || Navigator.msGetUserMedia != null;
    }

    public void addOverlay(MaterialWidget overlay) {
        overlayPanel.add(overlay);
    }

    public void removeOverlay(MaterialWidget overlay) {
        overlayPanel.remove(overlay);
    }

    public void clearOverlays() {
        overlayPanel.clear();
    }

    public MaterialWidget getVideo() {
        return video;
    }

    public void setVideo(MaterialWidget video) {
        this.video = video;
    }

    /**
     * Set the resolution of the camera
     */
    public void setResolution(int width, int height) {
        this.width = width;
        this.height = height;
        reload();
    }

    /**
     * Set the facing mode of the camera (Best usecase for Mobile Devices)
     */
    public void setFacingMode(CameraFacingMode facingMode) {
        this.facingMode = facingMode;
        reload();
    }

    /**
     * Called by the component when the stream has started.
     */
    protected void onCameraCaptureLoad() {
        CameraCaptureEvent.fire(this, CaptureStatus.STARTED);
    }

    /**
     * Called  by the component when the stream has paused.
     */
    protected void onCameraCapturePause() {
        CameraCaptureEvent.fire(this, CaptureStatus.PAUSED);
    }

    /**
     * Called by the component when the stream when an error occurs.
     */
    protected void onCameraCaptureError(String error) {
        CameraCaptureEvent.fire(this, error);
    }

    @Override
    public HandlerRegistration addCameraCaptureHandler(final CameraCaptureHandler handler) {
        return addHandler(event -> {
            if (isEnabled()) {
                handler.onCameraCaptureChange(event);
            }
        }, CameraCaptureEvent.getType());
    }
}