org.mariotaku.twidere.activity.ImageViewerActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.mariotaku.twidere.activity.ImageViewerActivity.java

Source

/*
 *            Twidere - Twitter client for Android
 * 
 * Copyright (C) 2012 Mariotaku Lee <mariotaku.lee@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.mariotaku.twidere.activity;

import static org.mariotaku.twidere.util.Utils.copyStream;
import static org.mariotaku.twidere.util.Utils.getBestCacheDir;
import static org.mariotaku.twidere.util.Utils.getImageLoaderHttpClient;
import static org.mariotaku.twidere.util.Utils.getRedirectedHttpResponse;
import static org.mariotaku.twidere.util.Utils.parseString;
import static org.mariotaku.twidere.util.Utils.showErrorToast;
import it.sephiroth.android.library.imagezoom.ImageViewTouch;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;

import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.util.BitmapDecodeHelper;

import twitter4j.TwitterException;
import twitter4j.http.HttpClientWrapper;
import twitter4j.http.HttpResponse;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;

public class ImageViewerActivity extends FragmentActivity
        implements Constants, OnClickListener, LoaderCallbacks<ImageViewerActivity.ImageLoader.Result> {

    private ImageViewTouch mImageView;
    private View mProgress;
    private ImageButton mRefreshStopSaveButton;
    private boolean mImageLoaded;
    private File mImageFile;

    @Override
    public void onClick(final View view) {
        final Uri uri = getIntent().getData();
        switch (view.getId()) {
        case R.id.close: {
            onBackPressed();
            break;
        }
        case R.id.refresh_stop_save: {
            final LoaderManager lm = getSupportLoaderManager();
            if (!mImageLoaded && !lm.hasRunningLoaders()) {
                loadImage(false);
            } else if (!mImageLoaded && lm.hasRunningLoaders()) {
                stopLoading();
            } else if (mImageLoaded) {
                saveImage();
            }
            break;
        }
        case R.id.share: {
            if (uri == null) {
                break;
            }
            final Intent intent = new Intent(Intent.ACTION_SEND);
            if (mImageFile != null && mImageFile.exists()) {
                intent.setType("image/*");
                intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(mImageFile));
            } else {
                intent.setType("text/plain");
                intent.putExtra(Intent.EXTRA_TEXT, uri.toString());
            }
            startActivity(Intent.createChooser(intent, getString(R.string.share)));
            break;
        }
        case R.id.open_in_browser: {
            if (uri == null) {
                break;
            }
            final String scheme = uri.getScheme();
            if ("http".equals(scheme) || "https".equals(scheme)) {
                final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                intent.addCategory(Intent.CATEGORY_BROWSABLE);
                try {
                    startActivity(intent);
                } catch (final ActivityNotFoundException e) {
                    // Ignore.
                }
            }
            break;
        }
        }
    }

    @Override
    public void onContentChanged() {
        super.onContentChanged();
        mImageView = (ImageViewTouch) findViewById(R.id.image_viewer);
        mRefreshStopSaveButton = (ImageButton) findViewById(R.id.refresh_stop_save);
        mProgress = findViewById(R.id.progress);
    }

    @Override
    public Loader<ImageLoader.Result> onCreateLoader(final int id, final Bundle args) {
        mProgress.setVisibility(View.VISIBLE);
        mRefreshStopSaveButton.setImageResource(R.drawable.ic_menu_stop);
        final Uri uri = args != null ? (Uri) args.getParcelable(INTENT_KEY_URI) : null;
        return new ImageLoader(this, uri);
    }

    @Override
    public void onLoaderReset(final Loader<ImageLoader.Result> loader) {

    }

    @Override
    public void onLoadFinished(final Loader<ImageLoader.Result> loader, final ImageLoader.Result data) {
        if (data != null && data.bitmap != null) {
            mImageView.setImageBitmap(data.bitmap);
            mImageFile = data.file;
            mImageLoaded = true;
            mRefreshStopSaveButton.setImageResource(R.drawable.ic_menu_save);
        } else {
            mImageView.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.broken_image));
            mImageFile = null;
            mImageLoaded = false;
            mRefreshStopSaveButton.setImageResource(R.drawable.ic_menu_refresh);
            if (data != null) {
                showErrorToast(this, null, data.exception, true);
            }
        }
        mProgress.setVisibility(View.GONE);
    }

    @Override
    protected void onCreate(final Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.image_viewer);
        loadImage(true);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopLoading();
    }

    @Override
    protected void onNewIntent(final Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        loadImage(false);
    }

    private void loadImage(final boolean init) {
        getSupportLoaderManager().destroyLoader(0);
        final Uri uri = getIntent().getData();
        if (uri == null) {
            finish();
            return;
        }
        mImageView.setImageBitmap(null);
        final Bundle args = new Bundle();
        args.putParcelable(INTENT_KEY_URI, uri);
        if (init) {
            getSupportLoaderManager().initLoader(0, args, this);
        } else {
            getSupportLoaderManager().restartLoader(0, args, this);
        }
    }

    private void saveImage() {
        if (mImageFile != null && mImageFile.exists()) {
            final Uri uri = getIntent().getData();
            if (uri == null)
                return;
            final String file_name = uri.getLastPathSegment();
            final BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(mImageFile.getPath(), o);
            final String mime_type = o.outMimeType;
            String file_name_with_suffix = null;
            if (file_name.matches("(.*/)*.+\\.(png|jpg|gif|bmp|jpeg|PNG|JPG|JPEG|GIF|BMP)$")) {
                file_name_with_suffix = file_name;
            } else {
                if (mime_type == null)
                    return;
                if (mime_type.startsWith("image/") && !"image/*".equals(mime_type)) {
                    file_name_with_suffix = file_name + "." + mime_type.substring(5);
                }
            }
            final Intent intent = new Intent(INTENT_ACTION_SAVE_FILE);
            intent.setPackage(getPackageName());
            intent.putExtra(INTENT_KEY_FILE_SOURCE, mImageFile.getPath());
            intent.putExtra(INTENT_KEY_FILENAME, file_name_with_suffix);
            startActivity(intent);
        }
    }

    private void stopLoading() {
        getSupportLoaderManager().destroyLoader(0);
        if (!mImageLoaded) {
            mRefreshStopSaveButton.setImageResource(R.drawable.ic_menu_refresh);
            mImageView.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.refresh_image));
            mProgress.setVisibility(View.GONE);
        }
    }

    public static class ImageLoader extends AsyncTaskLoader<ImageLoader.Result> {

        private static final String CACHE_DIR_NAME = "cached_images";

        private final Uri mUri;
        private final Context mContext;
        private final HttpClientWrapper mClient;

        private File mCacheDir;

        public ImageLoader(final Context context, final Uri uri) {
            super(context);
            mContext = context;
            mUri = uri;
            mClient = getImageLoaderHttpClient(context);
            init();
        }

        @Override
        public Result loadInBackground() {

            if (mUri == null)
                return new Result(null, null, null);
            final String scheme = mUri.getScheme();
            if ("http".equals(scheme) || "https".equals(scheme)) {
                final String url = parseString(mUri.toString());
                if (url == null)
                    return new Result(null, null, null);
                if (mCacheDir == null || !mCacheDir.exists()) {
                    init();
                }
                final File cache_file = new File(mCacheDir, getURLFilename(url));

                // from SD cache
                final Bitmap cached_bitmap = decodeFile(cache_file);
                if (cached_bitmap != null)
                    return new Result(cached_bitmap, cache_file, null);
                // from web
                try {
                    final HttpResponse resp = getRedirectedHttpResponse(mClient, url);
                    if (resp == null)
                        return null;
                    final InputStream is = resp.asStream();
                    final OutputStream os = new FileOutputStream(cache_file);
                    copyStream(is, os);
                    os.close();
                    final Bitmap bitmap = decodeFile(cache_file);
                    if (bitmap == null) {
                        // The file is corrupted, so we remove it from
                        // cache.
                        if (cache_file.isFile()) {
                            cache_file.delete();
                        }
                    }
                    return new Result(bitmap, cache_file, null);
                } catch (final FileNotFoundException e) {
                    init();
                } catch (final IOException e) {
                    return new Result(null, null, e);
                } catch (final TwitterException e) {
                    return new Result(null, null, e);
                }
            } else if ("file".equals(scheme)) {
                final File file = new File(mUri.getPath());
                return new Result(decodeFile(file), file, null);
            }
            return new Result(null, null, null);
        }

        @Override
        protected void onStartLoading() {
            forceLoad();
        }

        private Bitmap decodeFile(final File f) {
            if (f == null)
                return null;
            final int max_texture_size = getMaximumTextureSize();
            final BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(f.getPath(), o);
            if (o.outHeight <= 0)
                return null;
            final BitmapFactory.Options o1 = new BitmapFactory.Options();
            final double size = Math.max(o.outWidth, o.outHeight);
            o1.inSampleSize = size > max_texture_size ? (int) Math.round(size / max_texture_size) + 1 : 1;
            Bitmap bitmap = null;
            while (bitmap == null) {
                try {
                    final BitmapFactory.Options o2 = new BitmapFactory.Options();
                    o2.inSampleSize = o1.inSampleSize;
                    bitmap = BitmapDecodeHelper.decode(f.getPath(), o2);
                } catch (final OutOfMemoryError e) {
                    o1.inSampleSize++;
                    continue;
                }
                if (bitmap == null) {
                    break;
                }
                return bitmap;
            }
            return null;
        }

        private String getURLFilename(final String url) {
            if (url == null)
                return null;
            return url.replaceFirst("https?:\\/\\/", "").replaceAll("[^a-zA-Z0-9]", "_");
        }

        private void init() {
            /* Find the dir to save cached images. */
            mCacheDir = getBestCacheDir(mContext, CACHE_DIR_NAME);
            if (mCacheDir != null && !mCacheDir.exists()) {
                mCacheDir.mkdirs();
            }
        }

        public static int getMaximumTextureSize() {
            final EGL10 egl = (EGL10) EGLContext.getEGL();
            final EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

            // Initialise
            final int[] version = new int[2];
            egl.eglInitialize(display, version);

            // Query total number of configurations
            final int[] totalConfigurations = new int[1];
            egl.eglGetConfigs(display, null, 0, totalConfigurations);

            // Query actual list configurations
            final EGLConfig[] configurationsList = new EGLConfig[totalConfigurations[0]];
            egl.eglGetConfigs(display, configurationsList, totalConfigurations[0], totalConfigurations);

            final int[] textureSize = new int[1];
            int maximumTextureSize = 0;

            // Iterate through all the configurations to located the maximum
            // texture size
            for (int i = 0; i < totalConfigurations[0]; i++) {
                // Only need to check for width since opengl textures are always
                // squared
                egl.eglGetConfigAttrib(display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize);

                // Keep track of the maximum texture size
                if (maximumTextureSize < textureSize[0]) {
                    maximumTextureSize = textureSize[0];
                }

            }

            // Release
            egl.eglTerminate(display);
            return maximumTextureSize;
        }

        public static class Result {
            public final Bitmap bitmap;
            public final File file;
            public final Exception exception;

            public Result(final Bitmap bitmap, final File file, final Exception exception) {
                this.bitmap = bitmap;
                this.file = file;
                this.exception = exception;
            }
        }
    }
}