Search code examples
androidhtmlchromecastgoogle-castcustom-receiver

Chromecast cast HTML object using custom receiver


I am developing an Android app and I need to send a PDF file to Chromecast so I decided to use a PDF decoding library which return a HTML object.

//Load Images:
private void pdfLoadImages(final byte[] data)
{
    try
    {
        // run async
        new AsyncTask<Void, Void, String>()
        {
            // create and show a progress dialog
            ProgressDialog progressDialog = ProgressDialog.show(Prueba_PDF.this, "", "Opening...");

            @Override
            protected void onPostExecute(String html)
            {
                //after async close progress dialog
                progressDialog.dismiss();
                //load the html in the webview
                wv.loadDataWithBaseURL("", html, "text/html","UTF-8", "");
            }

            @Override
            protected String doInBackground(Void... params)
            {
                try
                {
                    //create pdf document object from bytes
                    ByteBuffer bb = ByteBuffer.NEW(data);
                    PDFFile pdf = new PDFFile(bb);
                    //Get the first page from the pdf doc
                    PDFPage PDFpage = pdf.getPage(1, true);
                    //create a scaling value according to the WebView Width
                    final float scale = ViewSize / PDFpage.getWidth() * 0.95f;
                    //convert the page into a bitmap with a scaling value
                    Bitmap page = PDFpage.getImage((int)(PDFpage.getWidth() * scale), (int)(PDFpage.getHeight() * scale), null, true, true);
                    //save the bitmap to a byte array
                    ByteArrayOutputStream stream = new ByteArrayOutputStream();
                    page.compress(Bitmap.CompressFormat.PNG, 100, stream);
                    byte[] byteArray = stream.toByteArray();
                    stream.reset();
                    //convert the byte array to a base64 string
                    String base64 = Base64.encodeToString(byteArray, Base64.NO_WRAP);
                    //create the html + add the first image to the html
                    String html = "<!DOCTYPE html><html><body bgcolor=\"#b4b4b4\"><img src=\"data:image/png;base64,"+base64+"\" hspace=10 vspace=10><br>";
                    //loop though the rest of the pages and repeat the above
                    for(int i = 2; i <= pdf.getNumPages(); i++)
                    {
                        PDFpage = pdf.getPage(i, true);
                        page = PDFpage.getImage((int)(PDFpage.getWidth() * scale), (int)(PDFpage.getHeight() * scale), null, true, true);
                        page.compress(Bitmap.CompressFormat.PNG, 100, stream);
                        byteArray = stream.toByteArray();
                        stream.reset();
                        base64 = Base64.encodeToString(byteArray, Base64.NO_WRAP);
                        html += "<img src=\"data:image/png;base64,"+base64+"\" hspace=10 vspace=10><br>";
                    }
                    stream.close();
                    html += "</body></html>";
                    return html;

                }
                catch (Exception e)
                {
                    Log.d("error", e.toString());
                }
                return null;
            }
        }.execute();
        System.gc();// run GC
    }
    catch (Exception e)
    {
        Log.d("error", e.toString());
    }
}

Is it possible to send that HTML object to Chromecast using the sendMessage() method so I can display the converted PDF file in my TV? Should I change the message parameter to my html object?

private void sendMessage(String message) {
    if (apiClient != null && mHelloWorldChannel != null) {
        try {
            Cast.CastApi.sendMessage(apiClient, mHelloWorldChannel.getNamespace(), message)
                    .setResultCallback(
                            new ResultCallback<Status>() {
                                @Override
                                public void onResult(Status result) {
                                    if (!result.isSuccess()) {
                                        Log.e(TAG, "Sending message failed");
                                    }
                                }
                            });
        } catch (Exception e) {
            Log.e(TAG, "Exception while sending message", e);
        }
    }
}

I am using the custom receiver from the CastHelloText application for Android, but I am afraid this won't suit my needs.

<!--
Copyright (C) 2014 Google Inc. All Rights Reserved.

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.
-->
<!DOCTYPE html>
<html>
  <head>
<style type="text/css">
  body {
    overflow:hidden;
  }
  div{
    height:720PX;
    width:1280PX;
    text-align:center;
    border:0px solid silver;
    display: table-cell;
    vertical-align:middle;
    color:#FFFFFF;
    background-color:#000000;
    font-weight:bold;
    font-family:Verdana, Geneva, sans-serif;
    font-size:40px;
}
</style>
<title>Cast Hello Text</title>
  </head>
  <body>
<DIV id="message">Talk to me</DIV>
<script type="text/javascript" src="//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js"></script>
<script type="text/javascript">
  window.onload = function() {
    cast.receiver.logger.setLevelValue(0);
    window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
    console.log('Starting Receiver Manager');

    // handler for the 'ready' event
    castReceiverManager.onReady = function(event) {
      console.log('Received Ready event: ' + JSON.stringify(event.data));
      window.castReceiverManager.setApplicationState("Application status is ready...");
    };

    // handler for 'senderconnected' event
    castReceiverManager.onSenderConnected = function(event) {
      console.log('Received Sender Connected event: ' + event.data);
      console.log(window.castReceiverManager.getSender(event.data).userAgent);
    };

    // handler for 'senderdisconnected' event
    castReceiverManager.onSenderDisconnected = function(event) {
      console.log('Received Sender Disconnected event: ' + event.data);
      if (window.castReceiverManager.getSenders().length == 0) {
        window.close();
      }
    };

    // handler for 'systemvolumechanged' event
    castReceiverManager.onSystemVolumeChanged = function(event) {
      console.log('Received System Volume Changed event: ' + event.data['level'] + ' ' +
          event.data['muted']);
    };

    // create a CastMessageBus to handle messages for a custom namespace
    window.messageBus =
      window.castReceiverManager.getCastMessageBus(
          'urn:x-cast:com.google.cast.sample.helloworld');

    // handler for the CastMessageBus message event
    window.messageBus.onMessage = function(event) {
      console.log('Message [' + event.senderId + ']: ' + event.data);
      // display the message from the sender
      displayText(event.data);
      // inform all senders on the CastMessageBus of the incoming message event
      // sender message listener will be invoked
      window.messageBus.send(event.senderId, event.data);
    }

    // initialize the CastReceiverManager with an application status message
    window.castReceiverManager.start({statusText: "Application is starting"});
    console.log('Receiver Manager started');
  };

  // utility function to display the text message in the input field
  function displayText(text) {
    console.log(text);
    document.getElementById("message").innerHTML=text;
    window.castReceiverManager.setApplicationState(text);
  };
</script>

Since pdfLoadImages method also returns the PDF pages as images, would it be easier sending the PDF file this way?


Solution

  • There isn't an explicit method for sending HTML to a Cast device. There is a way to send text message to a Cast device but that has been designed for short messages. You should not use that to send any other content to a Cast device.

    The receiver can load HTML from a URL into the DOM dynamically using JavaScript. This will work like including a frame on a desktop browser, but you will have to host the HTML on a web server. You could have a web server on your mobile device but you should consider performance and security issues.

    A Chromecast device has limited resources and you should not send any content that might require lots of memory or heavy CPU processing. To display a PDF you might want to consider using the Remote Display API. You will have to render the PDF on your mobile device and then the Cast protocol would then render the view on the TV.