/* * Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org> * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ package org.geometerplus.android.fbreader.image; import android.app.Activity; import android.content.Intent; import android.graphics.*; import android.os.Bundle; import android.util.FloatMath; import android.view.*; import org.geometerplus.zlibrary.core.image.*; import org.geometerplus.zlibrary.core.util.ZLColor; import org.geometerplus.zlibrary.ui.android.library.ZLAndroidLibrary; import org.geometerplus.zlibrary.ui.android.image.ZLAndroidImageData; import org.geometerplus.zlibrary.ui.android.util.ZLAndroidColorUtil; import org.geometerplus.android.util.OrientationUtil; public class ImageViewActivity extends Activity { public static final String URL_KEY = "fbreader.imageview.url"; public static final String BACKGROUND_COLOR_KEY = "fbreader.imageview.background"; private Bitmap myBitmap; private ZLColor myBgColor; @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); requestWindowFeature(Window.FEATURE_NO_TITLE); final ZLAndroidLibrary library = (ZLAndroidLibrary)ZLAndroidLibrary.Instance(); final boolean showStatusBar = library.ShowStatusBarOption.getValue(); getWindow().setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN, showStatusBar ? 0 : WindowManager.LayoutParams.FLAG_FULLSCREEN ); Thread.setDefaultUncaughtExceptionHandler( new org.geometerplus.zlibrary.ui.android.library.UncaughtExceptionHandler(this) ); setContentView(new ImageView()); final Intent intent = getIntent(); myBgColor = new ZLColor( intent.getIntExtra(BACKGROUND_COLOR_KEY, new ZLColor(127, 127, 127).intValue()) ); final String url = intent.getStringExtra(URL_KEY); final String prefix = ZLFileImage.SCHEME + "://"; if (url != null && url.startsWith(prefix)) { final ZLFileImage image = ZLFileImage.byUrlPath(url.substring(prefix.length())); if (image == null) { // TODO: error message (?) finish(); } try { final ZLImageData imageData = ZLImageManager.Instance().getImageData(image); myBitmap = ((ZLAndroidImageData)imageData).getFullSizeBitmap(); } catch (Exception e) { // TODO: error message (?) e.printStackTrace(); finish(); } } else { // TODO: error message (?) finish(); } } @Override protected void onStart() { super.onStart(); OrientationUtil.setOrientation(this, getIntent()); } @Override protected void onNewIntent(Intent intent) { OrientationUtil.setOrientation(this, intent); } @Override protected void onDestroy() { super.onDestroy(); if (myBitmap != null) { myBitmap.recycle(); } myBitmap = null; } private class ImageView extends View { private final Paint myPaint = new Paint(); private volatile int myDx = 0; private volatile int myDy = 0; private volatile float myZoomFactor = 1.0f; ImageView() { super(ImageViewActivity.this); } @Override protected void onDraw(final Canvas canvas) { myPaint.setColor(ZLAndroidColorUtil.rgb(myBgColor)); final int w = getWidth(); final int h = getHeight(); canvas.drawRect(0, 0, w, h, myPaint); if (myBitmap == null || myBitmap.isRecycled()) { return; } final int bw = (int)(myBitmap.getWidth() * myZoomFactor); final int bh = (int)(myBitmap.getHeight() * myZoomFactor); final Rect src = new Rect(0, 0, (int)(w / myZoomFactor), (int)(h / myZoomFactor)); final Rect dst = new Rect(0, 0, w, h); if (bw <= w) { src.left = 0; src.right = myBitmap.getWidth(); dst.left = (w - bw) / 2; dst.right = dst.left + bw; } else { final int bWidth = myBitmap.getWidth(); final int pWidth = (int)(w / myZoomFactor); src.left = Math.min(bWidth - pWidth, Math.max((bWidth - pWidth) / 2 - myDx, 0)); src.right += src.left; } if (bh <= h) { src.top = 0; src.bottom = myBitmap.getHeight(); dst.top = (h - bh) / 2; dst.bottom = dst.top + bh; } else { final int bHeight = myBitmap.getHeight(); final int pHeight = (int)(h / myZoomFactor); src.top = Math.min(bHeight - pHeight, Math.max((bHeight - pHeight) / 2 - myDy, 0)); src.bottom += src.top; } canvas.drawBitmap(myBitmap, src, dst, myPaint); } private void shift(int dx, int dy) { if (myBitmap == null || myBitmap.isRecycled()) { return; } final int w = (int)(getWidth() / myZoomFactor); final int h = (int)(getHeight() / myZoomFactor); final int bw = myBitmap.getWidth(); final int bh = myBitmap.getHeight(); final int newDx, newDy; if (w < bw) { final int delta = (bw - w) / 2; newDx = Math.max(-delta, Math.min(delta, myDx + dx)); } else { newDx = myDx; } if (h < bh) { final int delta = (bh - h) / 2; newDy = Math.max(-delta, Math.min(delta, myDy + dy)); } else { newDy = myDy; } if (newDx != myDx || newDy != myDy) { myDx = newDx; myDy = newDy; postInvalidate(); } } private boolean myMotionControl; private int mySavedX; private int mySavedY; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getPointerCount()) { case 1: return onSingleTouchEvent(event); case 2: return onDoubleTouchEvent(event); default: return false; } } private boolean onSingleTouchEvent(MotionEvent event) { int x = (int)event.getX(); int y = (int)event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_UP: myMotionControl = false; break; case MotionEvent.ACTION_DOWN: myMotionControl = true; mySavedX = x; mySavedY = y; break; case MotionEvent.ACTION_MOVE: if (myMotionControl) { shift( (int)((x - mySavedX) / myZoomFactor), (int)((y - mySavedY) / myZoomFactor) ); } myMotionControl = true; mySavedX = x; mySavedY = y; break; } return true; } private float myStartPinchDistance2 = -1; private float myStartZoomFactor; private boolean onDoubleTouchEvent(MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_POINTER_UP: myStartPinchDistance2 = -1; break; case MotionEvent.ACTION_POINTER_DOWN: { final float diffX = event.getX(0) - event.getX(1); final float diffY = event.getY(0) - event.getY(1); myStartPinchDistance2 = Math.max(diffX * diffX + diffY * diffY, 10f); myStartZoomFactor = myZoomFactor; break; } case MotionEvent.ACTION_MOVE: { final float diffX = event.getX(0) - event.getX(1); final float diffY = event.getY(0) - event.getY(1); final float distance2 = Math.max(diffX * diffX + diffY * diffY, 10f); if (myStartPinchDistance2 < 0) { myStartPinchDistance2 = distance2; myStartZoomFactor = myZoomFactor; } else { myZoomFactor = myStartZoomFactor * FloatMath.sqrt(distance2 / myStartPinchDistance2); postInvalidate(); } } break; } return true; } } }