All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.codename1.impl.javase.cef.JavaCEFSEPort Maven / Gradle / Ivy

/*
 * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Codename One designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *  
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 * 
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 * Please contact Codename One through http://www.codenameone.com/ if you 
 * need additional information or have any questions.
 */
package com.codename1.impl.javase.cef;

import com.codename1.impl.javase.AbstractBrowserWindowSE;
import com.codename1.impl.javase.BrowserWindowFactory;
import com.codename1.impl.javase.fx.FXBrowserWindowSE;
import com.codename1.impl.javase.JavaSEPort;
import static com.codename1.impl.javase.JavaSEPort.checkForPermission;
import com.codename1.io.JSONParser;
import com.codename1.io.Log;
import com.codename1.io.Util;
import com.codename1.media.AbstractMedia;
import com.codename1.media.AsyncMedia;
import com.codename1.media.Media;
import com.codename1.ui.BrowserComponent;
import com.codename1.ui.CN;
import com.codename1.ui.Component;
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.Label;
import com.codename1.ui.PeerComponent;
import com.codename1.ui.events.ActionEvent;
import com.codename1.util.AsyncResource;
import java.awt.EventQueue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.List;
import java.util.Map;
import javax.swing.JFrame;

/**
 *
 * @author shannah
 */
public class JavaCEFSEPort extends JavaSEPort {
    
    private static boolean cefExists;
    
    @Override
    public void init(Object m) {
        super.init(m);
        try {
            Class.forName("org.cef.CefApp");
            
            cefExists = true;
        } catch (Throwable ex) {
        }
    }
    
    
    
    
    public boolean isNativeBrowserComponentSupported() {
        return cefExists && !blockNativeBrowser;
        //return false;
    }
    
    
    public PeerComponent createBrowserComponent(final Object parent) {
        boolean useWKWebView = "true".equals(Display.getInstance().getProperty("BrowserComponent.useWKWebView", "false"));
        if (useWKWebView) {
            if (!useWKWebViewChecked) {
                useWKWebViewChecked = true;
                Map m = Display.getInstance().getProjectBuildHints();
                if(m != null) {
                    if(!m.containsKey("ios.useWKWebView")) {
                        Display.getInstance().setProjectBuildHint("ios.useWKWebView", "true");
                    }
                }
            }
        }
        return createCEFBrowserComponent(parent);
        
    }
    
    protected BrowserWindowFactory createBrowserWindowFactory() {
        return new BrowserWindowFactory() {
            @Override
            public AbstractBrowserWindowSE createBrowserWindow(String startURL) {
                return new FXBrowserWindowSE(startURL);
            }

       };
    }
    
    public PeerComponent createCEFBrowserComponent(final Object parent) {
        Log.p("Attempting to create browser component", Log.DEBUG);
        final PeerComponent[] out = new PeerComponent[1];
        final Throwable[] error = new Throwable[1];
        if (!EventQueue.isDispatchThread()) {
            try {
                EventQueue.invokeAndWait(new Runnable() {
                    public void run() {
                            try {
                                out[0] = createCEFBrowserComponent(parent);
                            } catch (Throwable t) {
                                error[0] = t;
                            }
                        }
                });
            } catch (Throwable ex) {
                Log.e(ex);
                throw new RuntimeException("Failed to create CEF browser", ex);
            }
            if (error[0] != null) {
                Log.e(error[0]);
                throw new RuntimeException("Failed to create CEF browser", error[0]);
            }
            
            return out[0];
        } else {
            
            return CEFBrowserComponent.create((BrowserComponent)parent);
        }
    }

   
    
    class CodenameOneMediaPlayer extends AbstractMedia {
        //private java.util.Timer endMediaPoller;
        private Runnable onCompletion;
        private List completionHandlers;
        //private javafx.scene.media.MediaPlayer player;
        private CEFBrowserComponent bc;
//        private MediaPlayer player;
        private boolean realized = false;
        private boolean isVideo;
        //private javafx.embed.swing.JFXPanel videoPanel;
        private JFrame frm;
        private boolean playing = false;
        private boolean nativePlayerMode;
        private AsyncResource _callback;
        
        private int currTime;
        private int duration;
        private int volume;
        private String lastErrorMessage;

        private final Runnable deinitializeCallback = new Runnable() {
            public void run() {
                cleanup();
            }
        };
        
        
        /**
         * This is a callback for the JavaFX media player that is supposed to fire
         * when the media is paused.  Unfortunately this is unreliable as the status
         * events seem to stop working after the first time it is paused.  
         * We use a poller (the endMediaPoller timer) to track the status of the video
         * so that the change listeners are fired.  This really sucks!
         */
        private Runnable onPaused = new Runnable() {
            public void run() {
                
                playing = false;
                
                fireMediaStateChange(AsyncMedia.State.Paused);
            }
        };
        /**
         * This is a callback for the JavaFX media player that is supposed to fire
         * when the media is paused.  Unfortunately this is unreliable as the status
         * events seem to stop working after the first time it is paused.  
         * We use a poller (the endMediaPoller timer) to track the status of the video
         * so that the change listeners are fired.  This really sucks!
         */
        private Runnable onPlaying = new Runnable() {
            @Override
            public void run() {
                playing = true;
                
                fireMediaStateChange(AsyncMedia.State.Playing);
            }
            
        };
        
        /**
         * This is a callback for the JavaFX media player that is supposed to fire
         * when the media is paused.  Unfortunately this is unreliable as the status
         * events seem to stop working after the first time it is paused.  
         * We use a poller (the endMediaPoller timer) to track the status of the video
         * so that the change listeners are fired.  This really sucks!
         */
        private Runnable onError = new Runnable() {
            public void run() {
                if (_callback != null && !_callback.isDone()) {
                    _callback.error(new IOException(lastErrorMessage));
                    return;
                } else {
                    Log.p(lastErrorMessage);
                }
                fireMediaError(createMediaException(lastErrorMessage, MediaErrorType.Unknown));
                if (!playing) {
                    fireMediaStateChange(AsyncMedia.State.Playing);
                    fireMediaStateChange(AsyncMedia.State.Paused);
                }

            }
        };

        
        
        private boolean isPlayable(String uri) {
            return true;
        }
        
        public CodenameOneMediaPlayer(String uri, boolean isVideo, JFrame f,  final Runnable onCompletion, final AsyncResource callback) throws IOException {
            init(uri, isVideo, f, onCompletion, callback);
        }
        public void init(String uri, boolean isVideo, JFrame f,  final Runnable onCompletion, final AsyncResource callback) throws IOException {
            instance.addDeinitializeHook(deinitializeCallback);
            _callback = callback;
            final JSONParser parser = new JSONParser();
            String mediaTag = isVideo ? "video" : "audio";
            String style = "";
            String script = " ";
            String html = ""+style+"<"+mediaTag+" id='cn1Media' width='640' height='480' style='width:100%;height:100%' src='"+uri+"'/>"+script+"";
            
            
            final String url = "data:text/html,"+Util.encodeUrl(html);
            bc = CEFBrowserComponent.create(url, new CEFBrowserComponentListener() {
                @Override
                public void onLoad(ActionEvent e) {
                    if (!callback.isDone()) {
                        callback.complete(CodenameOneMediaPlayer.this);
                    }
                }

                @Override
                public void onError(ActionEvent e) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                    
                }

                @Override
                public void onStart(ActionEvent e) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public boolean shouldNavigate(final String url) {
                    if (url.startsWith("{")) {
                        CN.callSerially(new Runnable() {
                            public void run() {
                                try {
                                    Map m = parser.parseJSON(new StringReader(url));
                                    if (m.containsKey("time")) {
                                        currTime = ((Number)m.get("time")).intValue();
                                    }
                                    if (m.containsKey("duration")) {
                                        duration = ((Number)m.get("duration")).intValue();
                                    }
                                    if (m.containsKey("volume")) {
                                        volume = ((Number)m.get("volume")).intValue();
                                    }
                                    if (m.containsKey("state")) {
                                        String mState = (String)m.get("state");
                                        if ("playing".equals(mState)) {
                                            if (!playing) {
                                                onPlaying.run();
                                            }
                                        } else if ("paused".equals(mState)) {
                                            if (playing) {
                                                onPaused.run();
                                            }
                                        } else if ("ended".equals(mState)) {
                                            if (playing) {
                                                onPaused.run();
                                            }
                                            CodenameOneMediaPlayer.this.onCompletion.run();
                                        } else if ("canplay".equals(mState)) {
                                            
                                        }
                                    }
                                    if (m.containsKey("error")) {
                                        
                                        lastErrorMessage = String.valueOf(m.get("error"));
                                        onError.run();
                                    }
                                } catch (IOException ex) {
                                    Log.e(ex);
                                }

                            }
                            
                        });
                        return false;
                        
                    }
                    return true;
                }
                
                
                
            });
            
            
            if (onCompletion != null) {
                addCompletionHandler(onCompletion);
            }
            
            this.onCompletion = new Runnable() {

                @Override
                public void run() {
                    if (callback != null && !callback.isDone()) {
                        callback.complete(CodenameOneMediaPlayer.this);
                    }
                    playing = false;
                    
                    fireMediaStateChange(AsyncMedia.State.Paused);
                    fireCompletionHandlers();
                }
                
            };
            this.isVideo = isVideo;
            this.frm = f;
            
        }

        private com.codename1.media.AsyncMedia.MediaException createMediaException(String message, AsyncMedia.MediaErrorType type) {
            
            return new com.codename1.media.AsyncMedia.MediaException(type, message);
        }
        
      
        
        public CodenameOneMediaPlayer(InputStream stream, String mimeType, JFrame f, final Runnable onCompletion, final AsyncResource callback) throws IOException {
            String suffix = guessSuffixForMimetype(mimeType);

            File temp = File.createTempFile("mtmp", suffix);
            temp.deleteOnExit();
            FileOutputStream out = new FileOutputStream(temp);
            byte buf[] = new byte[1024];
            int len = 0;
            while ((len = stream.read(buf, 0, buf.length)) > -1) {
                out.write(buf, 0, len);
            }
            stream.close();

            
            this.isVideo = mimeType.contains("video");
            this.frm = f;
            init(temp.getAbsolutePath(), isVideo, f, onCompletion, callback);
            
        }

        
        private void fireCompletionHandlers() {
            if (completionHandlers != null && !completionHandlers.isEmpty()) {
                
                Display.getInstance().callSerially(new Runnable() {

                    @Override
                    public void run() {
                        if (completionHandlers != null && !completionHandlers.isEmpty()) {
                            List  toRun;

                            synchronized(CodenameOneMediaPlayer.this) {
                                toRun = new java.util.ArrayList(completionHandlers);
                            }
                            for (Runnable r : toRun) {
                                r.run();
                            }
                        }
                    }

                });
            }
        }
        
        public void addCompletionHandler(Runnable onCompletion) {
            synchronized(this) {
                if (completionHandlers == null) {
                    completionHandlers = new java.util.ArrayList();
                }

                completionHandlers.add(onCompletion);
            }
        }

        public void removeCompletionHandler(Runnable onCompletion) {
            if (completionHandlers != null) {
                synchronized(this) {
                    completionHandlers.remove(onCompletion);
                }
            }
        }
        
        public void cleanup() {
            instance.removeDeinitializeHook(deinitializeCallback);
            pause();
            if (bc != null) {
                bc.cleanup();
                bc = null;
            }
        }

        public void prepare() {
        }
        
        @Override
        protected void playImpl() {
            
            if (isVideo && nativePlayerMode) {
                // To simulate native player mode, we will show a form with the player.
                final Form currForm = Display.getInstance().getCurrent();
                Form playerForm = new Form("Video Player", new com.codename1.ui.layouts.BorderLayout()) {

                    @Override
                    protected void onShow() {
                        EventQueue.invokeLater(new Runnable() {
                            public void run() {
                                playInternal();
                            }

                        });
                    }
                    
                };
                com.codename1.ui.Toolbar tb = new com.codename1.ui.Toolbar();
                playerForm.setToolbar(tb);
                tb.setBackCommand("Back", new com.codename1.ui.events.ActionListener() {
                    public void actionPerformed(com.codename1.ui.events.ActionEvent e) {
                        pauseInternal();
                        currForm.showBack();
                    }
                });
                
                Component videoComponent = getVideoComponent();
                if (videoComponent.getComponentForm() != null) {
                    videoComponent.remove();
                }
                playerForm.addComponent(com.codename1.ui.layouts.BorderLayout.CENTER, videoComponent);
                playerForm.show();
                return;
                
            }
            playInternal();
            
        }

        private void playInternal() {
            if (bc == null) {
                return;
            }
            bc.execute("cn1Media.play()");
            
        }
        
        private void pauseInternal() {
            if (bc == null) {
                return;
            }
            bc.execute("cn1Media.pause()");
            
            
        
        }
        
        
        
        
        @Override
        protected void pauseImpl() {
            if (bc == null) {
                return;
            }
            if(playing) {
                bc.execute("cn1Media.pause()");
            }
            //playing = false;
            
        }

        public int getTime() {
            if (bc == null) {
                return -1;
            }
            return currTime;
        }

        public void setTime(final int time) {
            if (bc == null) {
                return;
            }
            bc.execute("cn1Media.currentTime = "+(time/1000.0)+";");
           
            
        }

        public int getDuration() {
            int d = duration;
            if(d == 0){
                return -1;
            }
            return d;
        }

        public void setVolume(int vol) {
            if (bc == null) {
                return;
            }
            bc.execute("cn1Media.volume = "+((double) vol / 100d));
        }

        public int getVolume() {
            return volume;
        }


        
        @Override
        public com.codename1.ui.Component getVideoComponent() {
            if (!isVideo) {
                return new Label();
            } else {
                return bc;
            }
           
            
        }

        public boolean isVideo() {
            return isVideo;
        }

        public boolean isFullScreen() {
            return false;
        }

        public void setFullScreen(boolean fullScreen) {
        }

        @Override
        public boolean isPlaying() {
            return playing;
        }

        @Override
        public void setNativePlayerMode(boolean nativePlayer) {
            nativePlayerMode = nativePlayer;
        }

        @Override
        public boolean isNativePlayerMode() {
            return nativePlayerMode;
        }

        public void setVariable(String key, Object value) {
        }

        public Object getVariable(String key) {
            return null;
        }
    }

    @Override
    public AsyncResource createMediaAsync(InputStream inputStream, String mimeType, Runnable onCompletion) {
        final AsyncResource out = new AsyncResource();

        if(!checkForPermission("android.permission.READ_PHONE_STATE", "This is required to play media")){
            out.error(new IOException("android.permission.READ_PHONE_STATE is required to play media"));
            return out;
        }
        if(!checkForPermission("android.permission.WRITE_EXTERNAL_STORAGE", "This is required to play media")){
            out.error(new IOException("android.permission.WRITE_EXTERNAL_STORAGE is required to play media"));
            return out;
        }
        
        java.awt.Container cnt = canvas.getParent();
        while (!(cnt instanceof JFrame)) {
            cnt = cnt.getParent();
            if (cnt == null) {
                out.error(new RuntimeException("Could not find canvas.  Cannot create media"));
                return out;
            }
        }
        
        StreamWrapper stream = new StreamWrapper(inputStream, mimeType , -1);
        String id = BrowserPanel.getStreamRegistry().registerStream(stream);
        String uriAddress = "https://cn1app/streams/"+id;
        try {
            new CodenameOneMediaPlayer(uriAddress, mimeType.startsWith("video/"), (JFrame)cnt, onCompletion, out);
        } catch (IOException ex) {
            out.error(ex);
        }
        return out;
        
    }
    
    
    

    @Override
    public AsyncResource createMediaAsync(String uriAddress, boolean isVideo, Runnable onCompletion) {
        final AsyncResource out = new AsyncResource();

        if(!checkForPermission("android.permission.READ_PHONE_STATE", "This is required to play media")){
            out.error(new IOException("android.permission.READ_PHONE_STATE is required to play media"));
            return out;
        }
        if(!checkForPermission("android.permission.WRITE_EXTERNAL_STORAGE", "This is required to play media")){
            out.error(new IOException("android.permission.WRITE_EXTERNAL_STORAGE is required to play media"));
            return out;
        }
        
        java.awt.Container cnt = canvas.getParent();
        while (!(cnt instanceof JFrame)) {
            cnt = cnt.getParent();
            if (cnt == null) {
                out.error(new RuntimeException("Could not find canvas.  Cannot create media"));
                return out;
            }
        }
        
        if(uriAddress.startsWith("file:")) {
            uriAddress = unfile(uriAddress);
            File f = new File(uriAddress);
            
            if (f.exists()) {
                
                try {
                    FileInputStream fis = new FileInputStream(f);
                    StreamWrapper stream = new StreamWrapper(fis, getMimetype(f), f.length());
                    String id = BrowserPanel.getStreamRegistry().registerStream(stream);
                    //uriAddress = InputStreamSchemeHandler.getURL(id);
                    uriAddress = "https://cn1app/streams/"+id;
                    //new CodenameOneMediaPlayer(stream, ((JFrame)cnt), onCompletion, out);
                } catch (IOException ex) {
                    out.error(ex);
                    return out;
                }
                //uriAddress = "file://"+f.getAbsolutePath().replace('\\', '/');
                
                
            } else {
                out.error(new FileNotFoundException(uriAddress));
                return out;
            }
        }
        
        
        final String uri = uriAddress;
        
        
        try {
            new CodenameOneMediaPlayer(uri, isVideo, (JFrame)cnt, onCompletion, out);
        } catch (IOException ex) {
            out.error(ex);
        }
        return out;
    }

    @Override
    public void addCompletionHandler(Media media, Runnable onCompletion) {
        super.addCompletionHandler(media, onCompletion);
        if (media instanceof CodenameOneMediaPlayer) {
            ((CodenameOneMediaPlayer)media).addCompletionHandler(onCompletion);
        }
    }

    @Override
    public void removeCompletionHandler(Media media, Runnable onCompletion) {
        super.removeCompletionHandler(media, onCompletion); 
        if (media instanceof CodenameOneMediaPlayer) {
            ((CodenameOneMediaPlayer)media).removeCompletionHandler(onCompletion);
        }
    }
    
    
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy