im.neon.adapters.VectorMediasViewerAdapter.java Source code

Java tutorial

Introduction

Here is the source code for im.neon.adapters.VectorMediasViewerAdapter.java

Source

/*
 * Copyright 2015 OpenMarket 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 im.neon.adapters;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.support.v4.view.PagerAdapter;
import android.text.TextUtils;
import org.matrix.androidsdk.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.webkit.MimeTypeMap;
import android.webkit.WebView;
import android.widget.ImageView;
import android.widget.Toast;
import android.widget.VideoView;

import com.google.gson.JsonElement;

import org.matrix.androidsdk.MXSession;
import org.matrix.androidsdk.listeners.MXMediaDownloadListener;
import org.matrix.androidsdk.rest.model.MatrixError;
import org.matrix.androidsdk.rest.model.Message;
import org.matrix.androidsdk.util.ImageUtils;
import org.matrix.androidsdk.util.JsonUtils;
import org.matrix.androidsdk.view.PieFractionView;
import im.neon.R;

import org.matrix.androidsdk.db.MXMediasCache;

import im.neon.activity.CommonActivityUtils;
import im.neon.util.SlidableMediaInfo;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * An images slider
 */
public class VectorMediasViewerAdapter extends PagerAdapter {
    private static final String LOG_TAG = "MediasViewerAdapter";

    private Context mContext;
    private LayoutInflater mLayoutInflater;

    // medias
    private List<SlidableMediaInfo> mMediasMessagesList = null;
    private int mMaxImageWidth;
    private int mMaxImageHeight;
    private int mLatestPrimaryItemPosition = -1;
    private View mLatestPrimaryView = null;
    private MXMediasCache mMediasCache;
    private ArrayList<Integer> mHighResMediaIndex = new ArrayList<>();
    // current playing video
    private VideoView mPlayingVideoView = null;
    private MXSession mSession;

    private int mAutoPlayItemAt = -1;

    public VectorMediasViewerAdapter(Context context, MXSession session, MXMediasCache mediasCache,
            List<SlidableMediaInfo> mediaMessagesList, int maxImageWidth, int maxImageHeight) {
        this.mContext = context;
        this.mSession = session;
        this.mMediasMessagesList = mediaMessagesList;
        this.mMaxImageWidth = maxImageWidth;
        this.mMaxImageHeight = maxImageHeight;
        this.mLayoutInflater = LayoutInflater.from(context);
        this.mMediasCache = mediasCache;
    }

    @Override
    public int getCount() {
        return mMediasMessagesList.size();
    }

    @Override
    public void setPrimaryItem(ViewGroup container, final int position, Object object) {
        if (mLatestPrimaryItemPosition != position) {
            mLatestPrimaryItemPosition = position;

            final View view = (View) object;
            mLatestPrimaryView = view;

            view.post(new Runnable() {
                @Override
                public void run() {
                    stopPlayingVideo();
                }
            });

            view.post(new Runnable() {
                @Override
                public void run() {
                    if (mHighResMediaIndex.indexOf(position) < 0) {
                        downloadHighResMedia(view, position);
                    } else if (position == mAutoPlayItemAt) {
                        SlidableMediaInfo mediaInfo = mMediasMessagesList.get(position);

                        if (mediaInfo.mMessageType.equals(Message.MSGTYPE_VIDEO)) {
                            final VideoView videoView = (VideoView) view.findViewById(R.id.media_slider_videoview);
                            playVideo(view, videoView, mediaInfo.mMediaUrl, mediaInfo.mMimeType);
                        }

                        mAutoPlayItemAt = -1;
                    }
                }
            });
        }
    }

    /**
     *
     * @param position the position of the item to play.
     */
    public void autoPlayItemAt(int position) {
        mAutoPlayItemAt = position;
    }

    /**
     * Download the media if it was not yet done
     * @param view the slider page view
     * @param position the item position
     */
    private void downloadHighResMedia(final View view, final int position) {
        SlidableMediaInfo imageInfo = mMediasMessagesList.get(position);

        // image
        if (imageInfo.mMessageType.equals(Message.MSGTYPE_IMAGE)) {
            //
            if (TextUtils.isEmpty(imageInfo.mMimeType)) {
                imageInfo.mMimeType = "image/jpeg";
            }
            downloadHighResPict(view, position);
        } else {
            downloadVideo(view, position);
        }
    }

    /**
     * Download the video file.
     * The download will only start if the video should be auto played.
     * @param view the slider page view
     * @param position the item position
     */
    public void downloadVideo(final View view, final int position) {
        downloadVideo(view, position, false);
    }

    /**
     * Download the video file
     * @param view the slider page view
     * @param position the item position
     * @param force true to do not check the auto playmode
     */
    public void downloadVideo(final View view, final int position, boolean force) {
        final VideoView videoView = (VideoView) view.findViewById(R.id.media_slider_videoview);
        final ImageView thumbView = (ImageView) view.findViewById(R.id.media_slider_video_thumbnail);
        final PieFractionView pieFractionView = (PieFractionView) view.findViewById(R.id.media_slider_piechart);

        final SlidableMediaInfo mediaInfo = mMediasMessagesList.get(position);
        final String loadingUri = mediaInfo.mMediaUrl;
        final String thumbnailUrl = mediaInfo.mThumbnailUrl;

        // check if the media has been downloaded
        File file = mMediasCache.mediaCacheFile(loadingUri, mediaInfo.mMimeType);
        if (null != file) {
            mHighResMediaIndex.add(position);
            loadVideo(position, view, thumbnailUrl, Uri.fromFile(file).toString(), mediaInfo.mMimeType);

            if (position == mAutoPlayItemAt) {
                playVideo(view, videoView, mediaInfo.mMediaUrl, mediaInfo.mMimeType);
            }
            mAutoPlayItemAt = -1;
            return;
        }

        // the video download starts only when the user taps on click
        // let assumes it might configurable
        if (!force && (mAutoPlayItemAt != position)) {
            return;
        }

        // else download it
        String downloadId = mMediasCache.downloadMedia(mContext, mSession.getHomeserverConfig(), loadingUri,
                mediaInfo.mMimeType, mediaInfo.mEncryptedFileInfo);

        if (null != downloadId) {
            pieFractionView.setVisibility(View.VISIBLE);
            pieFractionView.setFraction(mMediasCache.getProgressValueForDownloadId(downloadId));
            pieFractionView.setTag(downloadId);

            mMediasCache.addDownloadListener(downloadId, new MXMediaDownloadListener() {

                @Override
                public void onDownloadError(String downloadId, JsonElement jsonElement) {
                    MatrixError error = JsonUtils.toMatrixError(jsonElement);

                    if ((null != error) && error.isSupportedErrorCode()) {
                        Toast.makeText(VectorMediasViewerAdapter.this.mContext, error.getLocalizedMessage(),
                                Toast.LENGTH_LONG).show();
                    }
                }

                @Override
                public void onDownloadProgress(String aDownloadId, DownloadStats stats) {
                    if (aDownloadId.equals(pieFractionView.getTag())) {
                        pieFractionView.setFraction(stats.mProgress);
                    }
                }

                @Override
                public void onDownloadComplete(String aDownloadId) {
                    if (aDownloadId.equals(pieFractionView.getTag())) {
                        pieFractionView.setVisibility(View.GONE);

                        final File mediaFile = mMediasCache.mediaCacheFile(loadingUri, mediaInfo.mMimeType);

                        if (null != mediaFile) {
                            mHighResMediaIndex.add(position);

                            Uri uri = Uri.fromFile(mediaFile);
                            final String newHighResUri = uri.toString();

                            thumbView.post(new Runnable() {
                                @Override
                                public void run() {
                                    loadVideo(position, view, thumbnailUrl, newHighResUri, mediaInfo.mMimeType);

                                    if (position == mAutoPlayItemAt) {
                                        playVideo(view, videoView, mediaInfo.mMediaUrl, mediaInfo.mMimeType);
                                        mAutoPlayItemAt = -1;
                                    }
                                }
                            });
                        }
                    }
                }
            });
        }
    }

    /**
     * Download the high res image
     * @param view the slider page view
     * @param position the item position
     */
    private void downloadHighResPict(final View view, final int position) {
        final WebView webView = (WebView) view.findViewById(R.id.media_slider_image_webview);
        final PieFractionView pieFractionView = (PieFractionView) view.findViewById(R.id.media_slider_piechart);
        final SlidableMediaInfo imageInfo = mMediasMessagesList.get(position);
        final String viewportContent = "width=640";
        final String loadingUri = imageInfo.mMediaUrl;
        final String downloadId = mMediasCache.loadBitmap(mContext, mSession.getHomeserverConfig(), loadingUri,
                imageInfo.mRotationAngle, imageInfo.mOrientation, imageInfo.mMimeType,
                imageInfo.mEncryptedFileInfo);

        webView.getSettings().setDisplayZoomControls(false);

        if (null != downloadId) {
            pieFractionView.setVisibility(View.VISIBLE);
            pieFractionView.setFraction(mMediasCache.getProgressValueForDownloadId(downloadId));
            mMediasCache.addDownloadListener(downloadId, new MXMediaDownloadListener() {
                @Override
                public void onDownloadError(String downloadId, JsonElement jsonElement) {
                    MatrixError error = JsonUtils.toMatrixError(jsonElement);

                    if ((null != error) && error.isSupportedErrorCode()) {
                        Toast.makeText(VectorMediasViewerAdapter.this.mContext, error.getLocalizedMessage(),
                                Toast.LENGTH_LONG).show();
                    }
                }

                @Override
                public void onDownloadProgress(String aDownloadId, DownloadStats stats) {
                    if (aDownloadId.equals(downloadId)) {
                        pieFractionView.setFraction(stats.mProgress);
                    }
                }

                @Override
                public void onDownloadComplete(String aDownloadId) {
                    if (aDownloadId.equals(downloadId)) {
                        pieFractionView.setVisibility(View.GONE);
                        final File mediaFile = mMediasCache.mediaCacheFile(loadingUri, imageInfo.mMimeType);

                        if (null != mediaFile) {
                            mHighResMediaIndex.add(position);

                            Uri uri = Uri.fromFile(mediaFile);
                            final String newHighResUri = uri.toString();

                            webView.post(new Runnable() {
                                @Override
                                public void run() {
                                    Uri mediaUri = Uri.parse(newHighResUri);
                                    // refresh the UI
                                    loadImage(webView, mediaUri, viewportContent,
                                            computeCss(newHighResUri, VectorMediasViewerAdapter.this.mMaxImageWidth,
                                                    VectorMediasViewerAdapter.this.mMaxImageHeight,
                                                    imageInfo.mRotationAngle));
                                }
                            });
                        }
                    }
                }
            });
        }
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, final int position) {
        View view = mLayoutInflater.inflate(R.layout.adapter_vector_medias_viewer, null, false);

        // hide the pie chart
        final PieFractionView pieFractionView = (PieFractionView) view.findViewById(R.id.media_slider_piechart);
        pieFractionView.setVisibility(View.GONE);

        final WebView imageWebView = (WebView) view.findViewById(R.id.media_slider_image_webview);
        final View videoLayout = view.findViewById(R.id.media_slider_videolayout);
        final ImageView thumbView = (ImageView) view.findViewById(R.id.media_slider_video_thumbnail);

        imageWebView.getSettings().setDisplayZoomControls(false);

        imageWebView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                VectorMediasViewerAdapter.this.onLongClick();
                return true;
            }
        });

        thumbView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                VectorMediasViewerAdapter.this.onLongClick();
                return true;
            }
        });

        // black background
        view.setBackgroundColor(0xFF000000);
        imageWebView.setBackgroundColor(0xFF000000);
        videoLayout.setBackgroundColor(0xFF000000);

        final SlidableMediaInfo mediaInfo = mMediasMessagesList.get(position);
        String mediaUrl = mediaInfo.mMediaUrl;

        if (mediaInfo.mMessageType.equals(Message.MSGTYPE_IMAGE)) {
            imageWebView.setVisibility(View.VISIBLE);
            imageWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
            imageWebView.getSettings().setJavaScriptEnabled(true);
            imageWebView.getSettings().setLoadWithOverviewMode(true);
            imageWebView.getSettings().setUseWideViewPort(true);
            imageWebView.getSettings().setBuiltInZoomControls(true);

            videoLayout.setVisibility(View.GONE);

            final int rotationAngle = mediaInfo.mRotationAngle;

            if (TextUtils.isEmpty(mediaInfo.mMimeType)) {
                mediaInfo.mMimeType = "image/jpeg";
            }

            final String mimeType = mediaInfo.mMimeType;
            File mediaFile = mMediasCache.mediaCacheFile(mediaUrl, mimeType);

            // is the high picture already downloaded ?
            if (null != mediaFile) {
                if (mHighResMediaIndex.indexOf(position) < 0) {
                    mHighResMediaIndex.add(position);
                }
            } else {
                // try to retrieve the thumbnail
                mediaFile = mMediasCache.mediaCacheFile(mediaUrl, mMaxImageWidth, mMaxImageHeight, null);
            }

            // the thumbnail is not yet downloaded
            if (null == mediaFile) {
                // display nothing
                container.addView(view, 0);
                return view;
            }

            String mediaUri = "file://" + mediaFile.getPath();

            String css = computeCss(mediaUri, mMaxImageWidth, mMaxImageHeight, rotationAngle);
            final String viewportContent = "width=640";
            loadImage(imageWebView, Uri.parse(mediaUri), viewportContent, css);
            container.addView(view, 0);
        } else {
            loadVideo(position, view, mediaInfo.mThumbnailUrl, mediaUrl, mediaInfo.mMimeType);
            container.addView(view, 0);
        }

        // check if the media is downloading
        String downloadId = mMediasCache.downloadMedia(mContext, mSession.getHomeserverConfig(), mediaUrl,
                mediaInfo.mMimeType, mediaInfo.mEncryptedFileInfo);

        if (null != downloadId) {
            pieFractionView.setVisibility(View.VISIBLE);
            pieFractionView.setFraction(mMediasCache.getProgressValueForDownloadId(downloadId));
            pieFractionView.setTag(downloadId);

            mMediasCache.addDownloadListener(downloadId, new MXMediaDownloadListener() {
                @Override
                public void onDownloadError(String downloadId, JsonElement jsonElement) {
                    pieFractionView.setVisibility(View.GONE);
                    MatrixError error = JsonUtils.toMatrixError(jsonElement);

                    if ((null != error) && error.isSupportedErrorCode()) {
                        Toast.makeText(VectorMediasViewerAdapter.this.mContext, error.getLocalizedMessage(),
                                Toast.LENGTH_LONG).show();
                    }
                }

                @Override
                public void onDownloadProgress(String aDownloadId, DownloadStats stats) {
                    if (aDownloadId.equals(pieFractionView.getTag())) {
                        pieFractionView.setFraction(stats.mProgress);
                    }
                }

                @Override
                public void onDownloadComplete(String aDownloadId) {
                    if (aDownloadId.equals(pieFractionView.getTag())) {
                        pieFractionView.setVisibility(View.GONE);
                    }
                }
            });
        }

        return view;
    }

    /**
     * Switch from the video view to the video thumbnail
     * @param view the page view
     * @param display trur to display the video thumbnail, false to display the video player
     */
    private void displayVideoThumbnail(final View view, boolean display) {
        final VideoView videoView = (VideoView) view.findViewById(R.id.media_slider_videoview);
        final ImageView thumbView = (ImageView) view.findViewById(R.id.media_slider_video_thumbnail);
        final ImageView playView = (ImageView) view.findViewById(R.id.media_slider_video_playView);

        videoView.setVisibility(display ? View.GONE : View.VISIBLE);
        thumbView.setVisibility(display ? View.VISIBLE : View.GONE);
        playView.setVisibility(display ? View.VISIBLE : View.GONE);
    }

    /**
     * Stop any playing video
     */
    public void stopPlayingVideo() {
        if (null != mPlayingVideoView) {
            mPlayingVideoView.stopPlayback();
            displayVideoThumbnail((View) (mPlayingVideoView.getParent()), true);
            mPlayingVideoView = null;
        }
    }

    /**
     * Play a video.
     * @param pageView the pageView
     * @param videoView the video view
     * @param videoUrl the video Url
     * @param videoMimeType the video mimetype
     */
    private void playVideo(View pageView, VideoView videoView, String videoUrl, String videoMimeType) {
        // init the video view only if there is a valid file
        // check if the media has been downloaded
        File srcFile = mMediasCache.mediaCacheFile(videoUrl, videoMimeType);

        if ((null != srcFile) && srcFile.exists()) {
            try {
                stopPlayingVideo();
                String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(videoMimeType);

                if (null != extension) {
                    extension += "." + extension;
                }

                // copy the media to ensure that it is deleted while playing
                File dstFile = new File(mContext.getCacheDir(), "sliderMedia" + extension);
                if (dstFile.exists()) {
                    dstFile.delete();
                }

                // Copy source file to destination
                FileInputStream inputStream = null;
                FileOutputStream outputStream = null;
                try {
                    // create only the
                    if (!dstFile.exists()) {
                        dstFile.createNewFile();

                        inputStream = new FileInputStream(srcFile);
                        outputStream = new FileOutputStream(dstFile);

                        byte[] buffer = new byte[1024 * 10];
                        int len;
                        while ((len = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, len);
                        }
                    }
                } catch (Exception e) {
                    Log.e(LOG_TAG, "## playVideo() : failed " + e.getMessage());
                    dstFile = null;
                } finally {
                    // Close resources
                    try {
                        if (inputStream != null)
                            inputStream.close();
                        if (outputStream != null)
                            outputStream.close();
                    } catch (Exception e) {
                        Log.e(LOG_TAG, "## playVideo() : failed " + e.getMessage());
                    }
                }

                // update the source
                videoView.setVideoPath(dstFile.getAbsolutePath());
                // hide the thumbnail
                displayVideoThumbnail(pageView, false);

                // let's playing
                mPlayingVideoView = videoView;
                videoView.start();

            } catch (Exception e) {
                Log.e(LOG_TAG, "## playVideo() : videoView.start(); failed " + e.getMessage());
            }
        }
    }

    /**
     * Download the current video file
     */
    public void downloadMedia() {
        final SlidableMediaInfo mediaInfo = mMediasMessagesList.get(mLatestPrimaryItemPosition);
        File file = mMediasCache.mediaCacheFile(mediaInfo.mMediaUrl, mediaInfo.mMimeType);

        if (null != file) {
            if (null != CommonActivityUtils.saveMediaIntoDownloads(mContext, file, null, mediaInfo.mMimeType)) {
                Toast.makeText(mContext, mContext.getText(R.string.media_slider_saved), Toast.LENGTH_LONG).show();
            }
        } else {
            downloadVideo(mLatestPrimaryView, mLatestPrimaryItemPosition, true);
            final String downloadId = mMediasCache.downloadMedia(mContext, mSession.getHomeserverConfig(),
                    mediaInfo.mMediaUrl, mediaInfo.mMimeType, mediaInfo.mEncryptedFileInfo);

            if (null != downloadId) {
                mMediasCache.addDownloadListener(downloadId, new MXMediaDownloadListener() {
                    @Override
                    public void onDownloadError(String downloadId, JsonElement jsonElement) {
                        MatrixError error = JsonUtils.toMatrixError(jsonElement);

                        if ((null != error) && error.isSupportedErrorCode()) {
                            Toast.makeText(VectorMediasViewerAdapter.this.mContext, error.getLocalizedMessage(),
                                    Toast.LENGTH_LONG).show();
                        }
                    }

                    @Override
                    public void onDownloadComplete(String aDownloadId) {
                        if (aDownloadId.equals(downloadId)) {
                            File file = mMediasCache.mediaCacheFile(mediaInfo.mMediaUrl, mediaInfo.mMimeType);
                            if (null != file) {
                                if (null != CommonActivityUtils.saveMediaIntoDownloads(mContext, file, null,
                                        mediaInfo.mMimeType)) {
                                    Toast.makeText(mContext, mContext.getText(R.string.media_slider_saved),
                                            Toast.LENGTH_LONG).show();
                                }
                            }
                        }
                    }
                });
            }
        }
    }

    /**
     * Long click management
     */
    private void onLongClick() {
        // The user is trying to leave with unsaved changes. Warn about that
        new AlertDialog.Builder(mContext).setMessage(R.string.media_slider_saved_message)
                .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        downloadMedia();
                    }
                }).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                }).create().show();
    }

    /**
     * Load the video items
     * @param view the page view
     * @param thumbnailUrl the thumbnail URL
     * @param videoUrl the video Url
     * @param videoMimeType the video mime type
     */
    private void loadVideo(final int position, final View view, final String thumbnailUrl, final String videoUrl,
            final String videoMimeType) {
        final VideoView videoView = (VideoView) view.findViewById(R.id.media_slider_videoview);
        final ImageView thumbView = (ImageView) view.findViewById(R.id.media_slider_video_thumbnail);
        final ImageView playView = (ImageView) view.findViewById(R.id.media_slider_video_playView);

        displayVideoThumbnail(view, !videoView.isPlaying());

        videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                mPlayingVideoView = null;
                displayVideoThumbnail(view, true);
            }
        });

        // the video is renderer in DSA so trap the on click on the video view parent
        ((View) videoView.getParent()).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopPlayingVideo();
                displayVideoThumbnail(view, true);
            }
        });

        // manage video error cases
        videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
            @Override
            public boolean onError(MediaPlayer mp, int what, int extra) {
                mPlayingVideoView = null;
                displayVideoThumbnail(view, true);
                return false;
            }
        });

        // init the thumbnail views
        mMediasCache.loadBitmap(mSession.getHomeserverConfig(), thumbView, thumbnailUrl, 0, 0, null, null);

        playView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // init the video view only if there is a valid file
                // check if the media has been downloaded
                File srcFile = mMediasCache.mediaCacheFile(videoUrl, videoMimeType);

                if (null != srcFile) {
                    playVideo(view, videoView, videoUrl, videoMimeType);
                } else {
                    mAutoPlayItemAt = position;
                    downloadVideo(view, position);
                }
            }
        });
    }

    /**
     * Update the image page.
     * @param webView the image is rendered in a webview.
     * @param imageUri the image Uri.
     * @param viewportContent  the viewport.
     * @param css the css.
     */
    private void loadImage(WebView webView, Uri imageUri, String viewportContent, String css) {
        String html = "<html><head><meta name='viewport' content='" + viewportContent + "'/>"
                + "<style type='text/css'>" + css + "</style></head>" + "<body> <div class='wrap'>" + "<img "
                + ("src='" + imageUri.toString() + "'") + " onerror='this.style.display=\"none\"' id='image' "
                + viewportContent + "/>" + "</div>" + "</body>" + "</html>";

        String mime = "text/html";
        String encoding = "utf-8";

        webView.loadDataWithBaseURL(null, html, mime, encoding, null);
        webView.requestLayout();
    }

    /**
     *  Image rendering subroutine
     */
    private String computeCss(String mediaUrl, int thumbnailWidth, int thumbnailHeight, int rotationAngle) {
        String css = "body { background-color: #000; height: 100%; width: 100%; margin: 0px; padding: 0px; }"
                + ".wrap { position: absolute; left: 0px; right: 0px; width: 100%; height: 100%; "
                + "display: -webkit-box; -webkit-box-pack: center; -webkit-box-align: center; "
                + "display: box; box-pack: center; box-align: center; } ";

        Uri mediaUri = null;

        try {
            mediaUri = Uri.parse(mediaUrl);
        } catch (Exception e) {
            Log.e(LOG_TAG, "## computeCss() : Uri.parse failed " + e.getMessage());
        }

        if (null == mediaUri) {
            return css;
        }

        // the rotation angle must be retrieved from the exif metadata
        if (rotationAngle == Integer.MAX_VALUE) {
            if (null != mediaUrl) {
                rotationAngle = ImageUtils.getRotationAngleForBitmap(mContext, mediaUri);
            }
        }

        if (rotationAngle != 0) {
            // get the image size to scale it to fill in the device screen.
            int imageWidth = thumbnailWidth;
            int imageHeight = thumbnailHeight;

            try {
                FileInputStream imageStream = new FileInputStream(new File(mediaUri.getPath()));
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                options.inPreferredConfig = Bitmap.Config.ARGB_8888;
                options.outWidth = -1;
                options.outHeight = -1;

                // get the full size bitmap
                Bitmap fullSizeBitmap = null;
                try {
                    fullSizeBitmap = BitmapFactory.decodeStream(imageStream, null, options);
                } catch (OutOfMemoryError e) {
                    Log.e(LOG_TAG, "## computeCss() : BitmapFactory.decodeStream failed " + e.getMessage());
                }

                imageWidth = options.outWidth;
                imageHeight = options.outHeight;

                imageStream.close();
                fullSizeBitmap.recycle();
            } catch (Exception e) {
                Log.e(LOG_TAG, "## computeCss() : failed " + e.getMessage());
            }

            String cssRotation = calcCssRotation(rotationAngle, imageWidth, imageHeight);

            css += "#image { " + cssRotation + " } ";
            css += "#thumbnail { " + cssRotation + " } ";
        }

        return css;
    }

    private String calcCssRotation(int rot, int imageWidth, int imageHeight) {
        if (rot == 90 || rot == 180 || rot == 270) {
            Point displaySize = getDisplaySize();
            double scale = Math.min((double) imageWidth / imageHeight, (double) displaySize.y / displaySize.x);

            final String rot180 = "-webkit-transform: rotate(180deg);";

            switch (rot) {
            case 90:
                return "-webkit-transform-origin: 50% 50%; -webkit-transform: rotate(90deg) scale(" + scale + " , "
                        + scale + ");";
            case 180:
                return rot180;
            case 270:
                return "-webkit-transform-origin: 50% 50%; -webkit-transform: rotate(270deg) scale(" + scale + " , "
                        + scale + ");";
            }
        }
        return "";
    }

    @SuppressLint("NewApi")
    private Point getDisplaySize() {
        Point size = new Point();
        WindowManager w = ((Activity) mContext).getWindowManager();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            w.getDefaultDisplay().getSize(size);
        } else {
            Display d = w.getDefaultDisplay();
            size.x = d.getWidth();
            size.y = d.getHeight();
        }

        return size;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }
}