MediaBrowserServlet.java :  » Web » i-jetty » org » mortbay » ijetty » console » Android Open Source

Android Open Source » Web » i jetty 
i jetty » org » mortbay » ijetty » console » MediaBrowserServlet.java
//========================================================================
//$Id$
//Copyright 2009 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//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.
//========================================================================

package org.mortbay.ijetty.console;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.ParseException;
import java.util.StringTokenizer;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.util.IO;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Environment;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.util.Config;
import android.util.Log;

public class MediaBrowserServlet extends HttpServlet
{
    public class MyMediaConnectorClient implements MediaScannerConnectionClient
    {
        private MediaScannerConnection _scanner = null;
        private final File _file;

        public MyMediaConnectorClient(File file)
        {
            _file = file;
        }

        public void onMediaScannerConnected()
        {
            _scanner.scanFile(_file.getAbsolutePath(),null);
        }

        public void onScanCompleted(String path, Uri uri)
        {
            Log.i(TAG,"Finished scanning!");
            _scanner.disconnect();
        }

        public void setScanner(MediaScannerConnection scanner)
        {
            _scanner = scanner;
        }
    }
    class PathTokenizer
    {
        private StringTokenizer tok;
        private String pathinfo;
        private int location;

        public PathTokenizer(String pathinfo)
        {
            this.pathinfo = pathinfo;
            this.tok = new StringTokenizer(pathinfo,"/");
            this.location = 0;
        }

        public String getPathinfo()
        {
            return pathinfo;
        }

        public String nextOptPathSegment() throws ParseException
        {
            if (tok.hasMoreTokens())
            {
                location++;
                String ret = tok.nextToken();
                if (ret == null)
                {
                    return null;
                }
                return ret.trim();
            }
            return null;
        }

        public String nextPathSegment() throws ParseException
        {
            if (tok.hasMoreTokens())
            {
                location++;
                String ret = tok.nextToken();
                if (ret == null)
                {
                    return null;
                }
                return ret.trim();
            }
            throw new ParseException("Missing required path segment",location);
        }
    }

    private static final long serialVersionUID = 1L;
    private static final String TAG = "MediaBrowserServlet";

    private static final String TYPE_VIDEO = "video";
    private static final String TYPE_AUDIO = "audio";
    private static final String TYPE_IMAGES = "images";
    private static final String LOCATION_EXTERNAL = "external";
    private static final String LOCATION_INTERNAL = "internal";

    private int __THUMB_WIDTH = 120;
    private int __THUMB_HEIGHT = 120;
    private ContentResolver resolver;
    private Context context;

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        String pathInfo = request.getPathInfo();

        if (pathInfo == null)
        {
            Log.w(TAG,"pathInfo was null, returning 404");
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        if (Config.LOGD)
        {
            Log.d(TAG,"PathInfo: " + pathInfo);
        }

        PathTokenizer pathtok = new PathTokenizer(pathInfo);
        try
        {
            String action = pathtok.nextPathSegment(); // 'json', 'fetch', or 'embed'

            if ("fetch".equals(action))
            {
                String type = pathtok.nextPathSegment();
                String location = pathtok.nextPathSegment();
                String item = pathtok.nextPathSegment();
                String thumb = pathtok.nextOptPathSegment();
                Uri contenturi = getContentUriByType(type,location);
                if (contenturi == null)
                {
                    response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                    return;
                }
                boolean asThumb = "thumb".equals(thumb);
                doGetFetchMedia(request,response,contenturi,item,asThumb);
            }
            else if ("json".equals(action.trim()))
            {
                String type = pathtok.nextPathSegment();
                doGetJson(request,response,type);
            }
            else if ("embed".equals(action.trim()))
            {
                String type = pathtok.nextPathSegment();
                String location = pathtok.nextPathSegment();
                String item = pathtok.nextPathSegment();
                doGetEmbedHtml(request,response,type,location,item);
            }
            else
            {
                Log.w(TAG,"invalid action - '" + action + "', returning 404");
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            }
        }
        catch (ParseException e)
        {
            Log.w(TAG,"Invalid request path: " + pathInfo,e);
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        }
    }

    /**
     * Get the HTML snippet for embedding the media content in a way suitable for the browser to view the content as
     *
     * @param request
     *            the incoming servlet request
     * @param response
     *            the outgoing servlet response
     * @param type
     *            the type of media interested in (<code>images</code>, <code>audio</code>, or <code>video</code>)
     * @param location
     *            the location of media interested in (<code>internal</code>, or <code>external</code>)
     * @param item
     *            the item reference
     * @throws ServletException
     * @throws IOException
     */
    public void doGetEmbedHtml(HttpServletRequest request, HttpServletResponse response, String type, String location, String item) throws ServletException,
            IOException
    {
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();

        String path = "/console/media/db/fetch/" + type + "/" + location + "/" + item;

        writer.print("<OBJECT ID='MediaPlayer' WIDTH='320' HEIGHT='26'");
        writer.println(" CLASSID='CLSID:22D6F312-B0F6-11D0-94AB-0080C74C7E95'");
        writer.println(" STANDBY='Loading...' TYPE='application/x-oleobject'>");
        writer.printf("  <PARAM NAME='FileName' VALUE='%s'>%n",path);
        writer.printf("  <EMBED TYPE='application/x-mplayer2' SRC='%s' NAME='MediaPlayer' WIDTH='320' HEIGHT='26' autostart='1'></EMBED>%n",path);
        writer.println("</OBJECT>");
    }

    public void doGetFetchMedia(HttpServletRequest request, HttpServletResponse response, Uri contenturi, String item, boolean asThumb)
            throws ServletException, IOException
    {
        try
        {
            Uri content = Uri.withAppendedPath(contenturi,item);

            if (asThumb)
            {
                Bitmap bitmap_orig = MediaStore.Images.Media.getBitmap(resolver,content);
                if (bitmap_orig != null)
                {
                    response.setStatus(HttpServletResponse.SC_OK);
                    InputStream stream = null;
                    OutputStream os = response.getOutputStream();
                    Bitmap bitmap = null;

                    int width = bitmap_orig.getWidth();
                    int height = bitmap_orig.getHeight();

                    // If the image is too big (AND the width/height isn't 0), scale it
                    if (((width > __THUMB_WIDTH) || (height > __THUMB_HEIGHT)) && (height != 0) && (width != 0))
                    {
                        float scaleWidth = 0;
                        float scaleHeight = 0;

                        if (Config.LOGD)
                        {
                            Log.d(TAG,"orig height = " + height + ", orig width = " + width);
                            Log.d(TAG,"__THUMB_HEIGHT = " + __THUMB_HEIGHT + ", __THUMB_WIDTH = " + __THUMB_WIDTH);
                        }

                        if (width > __THUMB_WIDTH)
                        {
                            scaleWidth = ((float)__THUMB_WIDTH) / width;
                        }

                        if (height > __THUMB_HEIGHT)
                        {
                            scaleHeight = ((float)__THUMB_HEIGHT) / height;
                        }

                        if (Config.LOGD)
                        {
                            Log.d(TAG,"scaleHeight = " + scaleHeight + ", scaleWidth = " + scaleWidth);
                        }

                        if ((scaleHeight < scaleWidth) && (scaleHeight != 0))
                        {
                            scaleWidth = scaleHeight;
                        }
                        else if ((scaleWidth < scaleHeight) && (scaleWidth != 0))
                        {
                            scaleHeight = scaleWidth;
                        }

                        if (scaleWidth == 0)
                        {
                            scaleWidth = scaleHeight;
                        }

                        if (scaleHeight == 0)
                        {
                            scaleHeight = scaleWidth;
                        }

                        if (Config.LOGD)
                        {
                            Log.d(TAG,"scaleHeight = " + scaleHeight + ", scaleWidth = " + scaleWidth + " (final)");
                        }

                        if (scaleHeight == 0)
                        {
                            Log.w(TAG,"scaleHeight and scaleWidth both = 0! Setting scale to 50%.");
                            scaleHeight = 0.5f;
                            scaleWidth = 0.5f;
                        }

                        Matrix matrix = new Matrix();
                        matrix.postScale(scaleWidth,scaleHeight);

                        // recreate the new Bitmap
                        bitmap = Bitmap.createBitmap(bitmap_orig,0,0,width,height,matrix,true);
                        //release original bitmap as soon as possible
                        bitmap_orig.recycle();
                        bitmap_orig = null;
                      
                
                        //TODO - if we updated to newer Api rev level we could use this
                        //Bitmap bitmap = MediaStore.Images.Thumbnails.getThumbnail(resolver, Integer.valueOf(item).intValue(), MediaStore.Images.Thumbnails.MINI_KIND, new BitmapFactory.Options());

                        ByteArrayOutputStream bytes = new ByteArrayOutputStream();

                        if (resolver.getType(content) == "image/gif")
                        {
                            Log.i(TAG,"Original image was gif, exporting thumb as JPEG as workaround");
                            response.setContentType("image/gif");
                            bitmap.compress(Bitmap.CompressFormat.JPEG,90,bytes);
                        }
                        else
                        {
                            Log.i(TAG,"Exporting thumb in png format");
                            response.setContentType("image/png");
                            bitmap.compress(Bitmap.CompressFormat.PNG,100,bytes);
                        }

                        stream = new ByteArrayInputStream(bytes.toByteArray());
                    }
                    else
                    {
                        if ((height == 0) || (width == 0))
                        {
                            Log.w(TAG,"Height or width were 0; sending original image instead!");
                        }
                        else
                        {
                            Log.i(TAG,"Original was smaller than " + __THUMB_HEIGHT + "x" + __THUMB_WIDTH + ", skipping scale.");
                        }
                        // just return the original data from the DB
                        response.setContentType(resolver.getType(content));
                        stream = resolver.openInputStream(content);
                    }

                    IO.copy(stream,os);
                }
            }
            else
            {
                Log.i(TAG,"Exporting original media");
                response.setContentType(resolver.getType(content));
                InputStream stream = resolver.openInputStream(content);
                OutputStream os = response.getOutputStream();

                response.setStatus(HttpServletResponse.SC_OK);
                IO.copy(stream,os);
            }
        }
        catch (Exception e)
        {
            Log.w(TAG,"Failed to fetch media",e);
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        }
    }

    /**
     * A request from the Javascript asking for some JSON describing the type of media being requested.
     *
     * @param request
     *            the incoming servlet request
     * @param response
     *            the outgoing servlet response
     * @param mediatype
     *            the type of media interested in (<code>images</code>, <code>audio</code>, or <code>video</code>)
     * @throws ServletException
     * @throws IOException
     */
    public void doGetJson(HttpServletRequest request, HttpServletResponse response, String mediatype) throws ServletException, IOException
    {
        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = response.getWriter();

        Uri[] mediaUris = getContentUrisByType(mediatype);

        writer.print("[ ");

        StringBuilder entry = new StringBuilder();

        int length = mediaUris.length;
        for (int iuri = 0; iuri < length; iuri++)
        {
            Uri contenturi = mediaUris[iuri];
            Log.d(TAG,"Using contenturi: " + contenturi);
            Cursor cur = resolver.query(contenturi,null,null,null,null);
            
            if (cur == null)
                continue; //skip - no results?
            
            String location = "";
            if (contenturi.toString().contains("/internal/"))
            {
                location = LOCATION_INTERNAL;
            }
            else if (contenturi.toString().contains("/external/"))
            {
                location = LOCATION_EXTERNAL;
            }
            else
            {
                Log.w(TAG,"Unknown content uri location (not internal or external?): " + contenturi);
                continue; // skip (don't know how to handle)
            }

            while (cur.moveToNext())
            {
                Long rowid = cur.getLong(cur.getColumnIndexOrThrow(BaseColumns._ID));
                String title = cur.getString(cur.getColumnIndexOrThrow(MediaStore.MediaColumns.TITLE));
                String displayname = cur.getString(cur.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME));
                String mimetype = cur.getString(cur.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE));
                String size = cur.getString(cur.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE));

                entry.setLength(0); // clear buffer.

                entry.append("{");
                entry.append(" 'type':").append(safeJson(mediatype));
                entry.append(",'location':").append(safeJson(location));
                entry.append(",'id':").append(safeJson(rowid));
                entry.append(",'title':").append(safeJson(title));
                entry.append(",'displayname':").append(safeJson(displayname));
                entry.append(",'mimetype':").append(safeJson(mimetype));
                entry.append(",'size':").append(safeJson(size));

                if ((contenturi == MediaStore.Audio.Media.EXTERNAL_CONTENT_URI) || (contenturi == MediaStore.Audio.Media.INTERNAL_CONTENT_URI))
                {
                    int music = cur.getInt(cur.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.IS_MUSIC));
                    if (music != 0)
                    {
                        String artist = cur.getString(cur.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.ARTIST));
                        String album = cur.getString(cur.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.ALBUM));

                        entry.append(",'artist':").append(safeJson(artist));
                        entry.append(",'album':").append(safeJson(album));
                    }
                }

                entry.append("}");
                Log.d(TAG,entry.toString());
                writer.print(entry.toString());

                if (!cur.isLast())
                {
                    writer.print(",");
                }
            }
            
            cur.close();
        }

        writer.print(" ]");
    }

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        response.setContentType("text/html");
        response.setStatus(HttpServletResponse.SC_OK);
        PrintWriter writer = response.getWriter();

        // Should we use just the servlet directory instead?
        File externalDir = Environment.getExternalStorageDirectory();
        File sdcarddir = new File(externalDir,"/jetty/media");

        // Create file upload directory if it doesn't exist
        if (!sdcarddir.exists())
        {
            sdcarddir.mkdir();
        }

        File output = null;

        try
        {
            // Save file to External Storage
            File file = (File)request.getAttribute("fileupload");
            String origName = request.getParameter("fileupload");

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            IO.copy(new FileInputStream(file),out);

            output = new File(sdcarddir,origName);
            Log.i(TAG,"Writing to: " + output);
            FileOutputStream stream = new FileOutputStream(output);

            out.writeTo(stream);
            stream.close();
        }
        catch (Exception e)
        {
            Log.w(TAG,"Failed to save uploaded file",e);
            printResponse(writer,1,"Could not save uploaded file to sdcard.",-1);
            return;
        }

        // Re-run media scanner, to re-detect media
        MyMediaConnectorClient client = new MyMediaConnectorClient(output);
        MediaScannerConnection scanner = new MediaScannerConnection(context,client);
        client.setScanner(scanner);

        scanner.connect();

        int filetype = -1;
        printResponse(writer,0,"No error",filetype);

    }

    public ContentResolver getContentResolver()
    {
        return resolver;
    }

    private Uri getContentUriByType(String mediatype, String location)
    {
        // Try by name
        if (TYPE_IMAGES.equals(mediatype))
        {
            if(LOCATION_INTERNAL.equals(location)) {
                return MediaStore.Images.Media.INTERNAL_CONTENT_URI;
            } else if(LOCATION_EXTERNAL.equals(location)) {
                return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else {
                Log.w(TAG,"Unknown location [" + location + "] for type [" + mediatype + "]");
                return null;
            }
        }
        else if (TYPE_AUDIO.equals(mediatype))
        {
            if(LOCATION_INTERNAL.equals(location)) {
                return MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
            } else if(LOCATION_EXTERNAL.equals(location)) {
                return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            } else {
                Log.w(TAG,"Unknown location [" + location + "] for type [" + mediatype + "]");
                return null;
            }
        }
        else if (TYPE_VIDEO.equals(mediatype))
        {
            if(LOCATION_INTERNAL.equals(location)) {
                return MediaStore.Video.Media.INTERNAL_CONTENT_URI;
            } else if(LOCATION_EXTERNAL.equals(location)) {
                return MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else {
                Log.w(TAG,"Unknown location [" + location + "] for type [" + mediatype + "]");
                return null;
            }
        }

        // Not a known name either. fail.
        Log.w(TAG,"Type String [" + mediatype + "] not a known ContentUri reference");
        return null;
    }

    /**
     * Fetch the list of content uris representing the basic media type.
     *
     * @param mediatype
     *            the basic media type to fetch
     * @return 2 Uri's representing the Content URIs for the [external, internal] content.
     */
    private Uri[] getContentUrisByType(String mediatype)
    {
        // Try by name
        if (TYPE_IMAGES.equals(mediatype))
        {
            return new Uri[]
            { MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media.INTERNAL_CONTENT_URI };
        }
        else if (TYPE_AUDIO.equals(mediatype))
        {
            return new Uri[]
            { MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, MediaStore.Audio.Media.INTERNAL_CONTENT_URI };
        }
        else if (TYPE_VIDEO.equals(mediatype))
        {
            return new Uri[]
            { MediaStore.Video.Media.EXTERNAL_CONTENT_URI, MediaStore.Video.Media.INTERNAL_CONTENT_URI };
        }

        // Not a known name either. fail.
        Log.w(TAG,"Type String [" + mediatype + "] not a known ContentUri reference");
        return null;
    }

    @Override
    public void init(ServletConfig config) throws ServletException
    {
        super.init(config);
        resolver = (ContentResolver)getServletContext().getAttribute("org.mortbay.ijetty.contentResolver");
        context = (Context)getServletContext().getAttribute("org.mortbay.ijetty.context");
    }

    private void printResponse(PrintWriter writer, int resp, String msg, int filetype)
    {
        writer.println("<script>");
        writer.println("var json = { error: " + resp + ", msg: '" + msg + "', filetype: " + filetype + " };");
        writer.println("if (top.Media) { top.Media.uploadComplete(json); }");
        writer.println("</script>");
    }

    private String safeJson(Number num)
    {
        if (num == null)
        {
            return "''";
        }
        return num.toString();
    }

    private String safeJson(String str)
    {
        if (str == null)
        {
            return "''";
        }
        return "'" + str.replaceAll("'","\\'") + "'";
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.