Compare commits
No commits in common. "master" and "feature/byte_array_support" have entirely different histories.
master
...
feature/by
|
|
@ -1,7 +1,7 @@
|
|||
apply plugin: 'com.android.library'
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion "25.0.0"
|
||||
compileSdkVersion 21
|
||||
buildToolsVersion "23.0.2"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
|
|
@ -17,8 +17,5 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
provided 'com.android.support:appcompat-v7:25.0.0'
|
||||
compile('com.github.chrisbanes:PhotoView:1.3.0') {
|
||||
exclude group: 'com.android.support', module: 'support-v4'
|
||||
}
|
||||
compile project(':libraries:Android-Pdf-Viewer-Library:gestureimageview')
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -54,7 +54,9 @@ import com.sun.pdfview.font.PDFFont;
|
|||
*/
|
||||
public class PDFParser extends BaseWatchable {
|
||||
|
||||
/** emit a file of DCT stream data. */
|
||||
/**
|
||||
* emit a file of DCT stream data.
|
||||
*/
|
||||
public final static String DEBUG_DCTDECODE_DATA = "debugdctdecode";
|
||||
static final boolean RELEASE = true;
|
||||
static final int PDF_CMDS_RANGE1_MIN = 1;
|
||||
|
|
@ -76,11 +78,13 @@ public class PDFParser extends BaseWatchable {
|
|||
private boolean resend = false;
|
||||
private Tok tok;
|
||||
private boolean catchexceptions; // Indicates state of BX...EX
|
||||
/** a weak reference to the page we render into. For the page
|
||||
/**
|
||||
* a weak reference to the page we render into. For the page
|
||||
* to remain available, some other code must retain a strong reference to it.
|
||||
*/
|
||||
private WeakReference pageRef;
|
||||
/** the actual command, for use within a singe iteration. Note that
|
||||
/**
|
||||
* the actual command, for use within a singe iteration. Note that
|
||||
* this must be released at the end of each iteration to assure the
|
||||
* page can be collected if not in use
|
||||
*/
|
||||
|
|
@ -88,7 +92,7 @@ public class PDFParser extends BaseWatchable {
|
|||
// ---- result variables
|
||||
byte[] stream;
|
||||
HashMap<String, PDFObject> resources;
|
||||
// public static int debuglevel = 4000;
|
||||
// public static int debuglevel = 4000;
|
||||
// TODO [FHe]: changed for debugging
|
||||
public static int debuglevel = -1;
|
||||
|
||||
|
|
@ -121,7 +125,7 @@ public class PDFParser extends BaseWatchable {
|
|||
* on to a PDFParser.
|
||||
*/
|
||||
public PDFParser(PDFPage cmds, byte[] stream,
|
||||
HashMap<String, PDFObject> resources) {
|
||||
HashMap<String, PDFObject> resources) {
|
||||
super();
|
||||
|
||||
this.pageRef = new WeakReference<PDFPage>(cmds);
|
||||
|
|
@ -137,43 +141,76 @@ public class PDFParser extends BaseWatchable {
|
|||
/////////////////////////////////////////////////////////////////
|
||||
// B E G I N R E A D E R S E C T I O N
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* a token from a PDF Stream
|
||||
*/
|
||||
static class Tok {
|
||||
|
||||
/** begin bracket < */
|
||||
/**
|
||||
* begin bracket <
|
||||
*/
|
||||
public static final int BRKB = 11;
|
||||
/** end bracket > */
|
||||
/**
|
||||
* end bracket >
|
||||
*/
|
||||
public static final int BRKE = 10;
|
||||
/** begin array [ */
|
||||
/**
|
||||
* begin array [
|
||||
*/
|
||||
public static final int ARYB = 9;
|
||||
/** end array ] */
|
||||
/**
|
||||
* end array ]
|
||||
*/
|
||||
public static final int ARYE = 8;
|
||||
/** String (, readString looks for trailing ) */
|
||||
/**
|
||||
* String (, readString looks for trailing )
|
||||
*/
|
||||
public static final int STR = 7;
|
||||
/** begin brace { */
|
||||
/**
|
||||
* begin brace {
|
||||
*/
|
||||
public static final int BRCB = 5;
|
||||
/** end brace } */
|
||||
/**
|
||||
* end brace }
|
||||
*/
|
||||
public static final int BRCE = 4;
|
||||
/** number */
|
||||
/**
|
||||
* number
|
||||
*/
|
||||
public static final int NUM = 3;
|
||||
/** keyword */
|
||||
/**
|
||||
* keyword
|
||||
*/
|
||||
public static final int CMD = 2;
|
||||
/** name (begins with /) */
|
||||
/**
|
||||
* name (begins with /)
|
||||
*/
|
||||
public static final int NAME = 1;
|
||||
/** unknown token */
|
||||
/**
|
||||
* unknown token
|
||||
*/
|
||||
public static final int UNK = 0;
|
||||
/** end of stream */
|
||||
/**
|
||||
* end of stream
|
||||
*/
|
||||
public static final int EOF = -1;
|
||||
/** the string value of a STR, NAME, or CMD token */
|
||||
/**
|
||||
* the string value of a STR, NAME, or CMD token
|
||||
*/
|
||||
public String name;
|
||||
/** the value of a NUM token */
|
||||
/**
|
||||
* the value of a NUM token
|
||||
*/
|
||||
public double value;
|
||||
/** the type of the token */
|
||||
/**
|
||||
* the type of the token
|
||||
*/
|
||||
public int type;
|
||||
|
||||
/** a printable representation of the token */
|
||||
/**
|
||||
* a printable representation of the token
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (type == NUM) {
|
||||
|
|
@ -372,7 +409,7 @@ public class PDFParser extends BaseWatchable {
|
|||
* character, which has already been read, and end with a balanced ')'
|
||||
* character. A '\' character starts an escape sequence of up
|
||||
* to three octal digits.</p>
|
||||
*
|
||||
* <p/>
|
||||
* <p>Parenthesis must be enclosed by a balanced set of parenthesis,
|
||||
* so a string may enclose balanced parenthesis.</p>
|
||||
*
|
||||
|
|
@ -384,7 +421,7 @@ public class PDFParser extends BaseWatchable {
|
|||
int parenLevel = 0;
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
|
||||
for (int to = stream_.length; loc < to;) {
|
||||
for (int to = stream_.length; loc < to; ) {
|
||||
int c = stream_[loc++];
|
||||
if (c == ')') {
|
||||
if (parenLevel-- == 0) {
|
||||
|
|
@ -425,7 +462,7 @@ public class PDFParser extends BaseWatchable {
|
|||
* character, which has already been read, and end with a '>'
|
||||
* character. Each byte in the array is made up of two hex characters,
|
||||
* the first being the high-order bit.
|
||||
*
|
||||
* <p/>
|
||||
* We translate the byte arrays into char arrays by combining two bytes
|
||||
* into a character, and then translate the character array into a string.
|
||||
* [JK FIXME this is probably a really bad idea!]
|
||||
|
|
@ -440,7 +477,7 @@ public class PDFParser extends BaseWatchable {
|
|||
char w = (char) 0;
|
||||
|
||||
// read individual bytes and format into a character array
|
||||
for (int to = stream_.length; (loc < to) && (stream_[loc] != '>');) {
|
||||
for (int to = stream_.length; (loc < to) && (stream_[loc] != '>'); ) {
|
||||
final char c = (char) stream_[loc];
|
||||
byte b = (byte) 0;
|
||||
|
||||
|
|
@ -478,6 +515,7 @@ public class PDFParser extends BaseWatchable {
|
|||
/////////////////////////////////////////////////////////////////
|
||||
// B E G I N P A R S E R S E C T I O N
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Called to prepare for some iterations
|
||||
*/
|
||||
|
|
@ -505,16 +543,16 @@ public class PDFParser extends BaseWatchable {
|
|||
/**
|
||||
* parse the stream. commands are added to the PDFPage initialized
|
||||
* in the constructor as they are encountered.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Page numbers in comments refer to the Adobe PDF specification.<br>
|
||||
* commands are listed in PDF spec 32000-1:2008 in Table A.1
|
||||
*
|
||||
* @return <ul><li>Watchable.RUNNING when there are commands to be processed
|
||||
* <li>Watchable.COMPLETED when the page is done and all
|
||||
* the commands have been processed
|
||||
* <li>Watchable.STOPPED if the page we are rendering into is
|
||||
* no longer available
|
||||
* </ul>
|
||||
* <li>Watchable.COMPLETED when the page is done and all
|
||||
* the commands have been processed
|
||||
* <li>Watchable.STOPPED if the page we are rendering into is
|
||||
* no longer available
|
||||
* </ul>
|
||||
*/
|
||||
public int iterate() throws Exception {
|
||||
// make sure the page is still available, and create the reference
|
||||
|
|
@ -662,7 +700,7 @@ public class PDFParser extends BaseWatchable {
|
|||
path = new Path();
|
||||
break;
|
||||
case 'f':
|
||||
// the fall-through is intended!
|
||||
// the fall-through is intended!
|
||||
case 'F':
|
||||
// fill the path (close/not close identical)
|
||||
cmds.addPath(path, PDFShapeCmd.FILL | clip);
|
||||
|
|
@ -952,11 +990,11 @@ public class PDFParser extends BaseWatchable {
|
|||
break;
|
||||
case 'Q' + ('q' << 8):
|
||||
processQCmd();
|
||||
// 'q'-cmd
|
||||
// push the parser state
|
||||
parserStates.push((ParserState) state.clone());
|
||||
// push graphics state
|
||||
cmds.addPush();
|
||||
// 'q'-cmd
|
||||
// push the parser state
|
||||
parserStates.push((ParserState) state.clone());
|
||||
// push graphics state
|
||||
cmds.addPush();
|
||||
break;
|
||||
default:
|
||||
if (catchexceptions) {
|
||||
|
|
@ -1025,6 +1063,7 @@ public class PDFParser extends BaseWatchable {
|
|||
path = null;
|
||||
cmds = null;
|
||||
}
|
||||
|
||||
boolean errorwritten = false;
|
||||
|
||||
public void dumpStreamToError() {
|
||||
|
|
@ -1068,10 +1107,12 @@ public class PDFParser extends BaseWatchable {
|
|||
/////////////////////////////////////////////////////////////////
|
||||
// H E L P E R S
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* get a property from a named dictionary in the resources of this
|
||||
* content stream.
|
||||
* @param name the name of the property in the dictionary
|
||||
*
|
||||
* @param name the name of the property in the dictionary
|
||||
* @param inDict the name of the dictionary in the resources
|
||||
* @return the value of the property in the dictionary
|
||||
*/
|
||||
|
|
@ -1092,6 +1133,7 @@ public class PDFParser extends BaseWatchable {
|
|||
* Insert a PDF object into the command stream. The object must
|
||||
* either be an Image or a Form, which is a set of PDF commands
|
||||
* in a stream.
|
||||
*
|
||||
* @param obj the object to insert, an Image or a Form.
|
||||
*/
|
||||
private void doXObject(PDFObject obj) throws IOException {
|
||||
|
|
@ -1111,8 +1153,9 @@ public class PDFParser extends BaseWatchable {
|
|||
/**
|
||||
* Parse image data into a Java BufferedImage and add the image
|
||||
* command to the page.
|
||||
*
|
||||
* @param obj contains the image data, and a dictionary describing
|
||||
* the width, height and color space of the image.
|
||||
* the width, height and color space of the image.
|
||||
*/
|
||||
private void doImage(PDFObject obj) throws IOException {
|
||||
cmds.addImage(PDFImage.createImage(obj, resources));
|
||||
|
|
@ -1122,8 +1165,9 @@ public class PDFParser extends BaseWatchable {
|
|||
* Inject a stream of PDF commands onto the page. Optimized to cache
|
||||
* a parsed stream of commands, so that each Form object only needs
|
||||
* to be parsed once.
|
||||
*
|
||||
* @param obj a stream containing the PDF commands, a transformation
|
||||
* matrix, bounding box, and resources.
|
||||
* matrix, bounding box, and resources.
|
||||
*/
|
||||
private void doForm(PDFObject obj) throws IOException {
|
||||
// check to see if we've already parsed this sucker
|
||||
|
|
@ -1186,6 +1230,7 @@ public class PDFParser extends BaseWatchable {
|
|||
//
|
||||
// return patternSpace.getPaint(pattern, components, resources);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Parse the next object out of the PDF stream. This could be a
|
||||
* Double, a String, a HashMap (dictionary), Object[] array, or
|
||||
|
|
@ -1198,13 +1243,13 @@ public class PDFParser extends BaseWatchable {
|
|||
case Tok.NUM:
|
||||
return new Double(tok.value);
|
||||
case Tok.STR:
|
||||
// the fall-through is intended!
|
||||
// the fall-through is intended!
|
||||
case Tok.NAME:
|
||||
return tok.name;
|
||||
case Tok.BRKB: {
|
||||
final HashMap<String, PDFObject> hm = new HashMap<String, PDFObject>();
|
||||
String name = null;
|
||||
for (Object obj = null; (obj = parseObject()) != null;) {
|
||||
for (Object obj = null; (obj = parseObject()) != null; ) {
|
||||
if (name == null) {
|
||||
name = (String) obj;
|
||||
} else {
|
||||
|
|
@ -1220,7 +1265,7 @@ public class PDFParser extends BaseWatchable {
|
|||
case Tok.ARYB: {
|
||||
// build an array
|
||||
final ArrayList<Object> ary = new ArrayList<Object>();
|
||||
for (Object obj = null; (obj = parseObject()) != null;) {
|
||||
for (Object obj = null; (obj = parseObject()) != null; ) {
|
||||
ary.add(obj);
|
||||
}
|
||||
if (tok.type != Tok.ARYE) {
|
||||
|
|
@ -1351,6 +1396,7 @@ public class PDFParser extends BaseWatchable {
|
|||
|
||||
/**
|
||||
* add graphics state commands contained within a dictionary.
|
||||
*
|
||||
* @param name the resource name of the graphics state dictionary
|
||||
*/
|
||||
private void setGSState(String name) throws IOException {
|
||||
|
|
@ -1408,11 +1454,15 @@ public class PDFParser extends BaseWatchable {
|
|||
|
||||
/**
|
||||
* pop a single float value off the stack.
|
||||
*
|
||||
* @return the float value of the top of the stack
|
||||
* @throws PDFParseException if the value on the top of the stack
|
||||
* isn't a number
|
||||
* isn't a number
|
||||
*/
|
||||
private float popFloat() throws PDFParseException {
|
||||
if (stack.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
Object obj = stack.pop();
|
||||
if (obj instanceof Double) {
|
||||
return ((Double) obj).floatValue();
|
||||
|
|
@ -1425,10 +1475,11 @@ public class PDFParser extends BaseWatchable {
|
|||
* pop an array of float values off the stack. This is equivalent
|
||||
* to filling an array from end to front by popping values off the
|
||||
* stack.
|
||||
*
|
||||
* @param count the number of numbers to pop off the stack
|
||||
* @return an array of length <tt>count</tt>
|
||||
* @throws PDFParseException if any of the values popped off the
|
||||
* stack are not numbers.
|
||||
* stack are not numbers.
|
||||
*/
|
||||
private float[] popFloat(int count) throws PDFParseException {
|
||||
float[] ary = new float[count];
|
||||
|
|
@ -1440,6 +1491,7 @@ public class PDFParser extends BaseWatchable {
|
|||
|
||||
/**
|
||||
* pop a single integer value off the stack.
|
||||
*
|
||||
* @return the integer value of the top of the stack
|
||||
* @throws PDFParseException if the top of the stack isn't a number.
|
||||
*/
|
||||
|
|
@ -1456,10 +1508,11 @@ public class PDFParser extends BaseWatchable {
|
|||
* pop an array of integer values off the stack. This is equivalent
|
||||
* to filling an array from end to front by popping values off the
|
||||
* stack.
|
||||
*
|
||||
* @param count the number of numbers to pop off the stack
|
||||
* @return an array of length <tt>count</tt>
|
||||
* @throws PDFParseException if any of the values popped off the
|
||||
* stack are not numbers.
|
||||
* stack are not numbers.
|
||||
*/
|
||||
private float[] popFloatArray() throws PDFParseException {
|
||||
Object obj = stack.pop();
|
||||
|
|
@ -1480,9 +1533,10 @@ public class PDFParser extends BaseWatchable {
|
|||
|
||||
/**
|
||||
* pop a String off the stack.
|
||||
*
|
||||
* @return the String from the top of the stack
|
||||
* @throws PDFParseException if the top of the stack is not a NAME
|
||||
* or STR.
|
||||
* or STR.
|
||||
*/
|
||||
private String popString() throws PDFParseException {
|
||||
Object obj = stack.pop();
|
||||
|
|
@ -1495,9 +1549,10 @@ public class PDFParser extends BaseWatchable {
|
|||
|
||||
/**
|
||||
* pop a PDFObject off the stack.
|
||||
*
|
||||
* @return the PDFObject from the top of the stack
|
||||
* @throws PDFParseException if the top of the stack does not contain
|
||||
* a PDFObject.
|
||||
* a PDFObject.
|
||||
*/
|
||||
private PDFObject popObject() throws PDFParseException {
|
||||
Object obj = stack.pop();
|
||||
|
|
@ -1509,9 +1564,10 @@ public class PDFParser extends BaseWatchable {
|
|||
|
||||
/**
|
||||
* pop an array off the stack
|
||||
*
|
||||
* @return the array of objects that is the top element of the stack
|
||||
* @throws PDFParseException if the top element of the stack does not
|
||||
* contain an array.
|
||||
* contain an array.
|
||||
*/
|
||||
private Object[] popArray() throws PDFParseException {
|
||||
Object obj = stack.pop();
|
||||
|
|
@ -1528,11 +1584,17 @@ public class PDFParser extends BaseWatchable {
|
|||
*/
|
||||
class ParserState implements Cloneable {
|
||||
|
||||
/** the fill color space */
|
||||
/**
|
||||
* the fill color space
|
||||
*/
|
||||
PDFColorSpace fillCS;
|
||||
/** the stroke color space */
|
||||
/**
|
||||
* the stroke color space
|
||||
*/
|
||||
PDFColorSpace strokeCS;
|
||||
/** the text paramters */
|
||||
/**
|
||||
* the text paramters
|
||||
*/
|
||||
PDFTextFormat textFormat;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* $Id: CalRGBColor.java,v 1.2 2007/12/20 18:33:34 rbair Exp $
|
||||
*
|
||||
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
|
||||
* Santa Clara, California 95054, U.S.A. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package com.sun.pdfview.colorspace;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
public class ICCIColorSpace extends PDFColorSpace {
|
||||
|
||||
public ICCIColorSpace() {
|
||||
}
|
||||
|
||||
/**
|
||||
* get the number of components (3)
|
||||
*/
|
||||
@Override public int getNumComponents() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override public int toColor(float[] fcomp) {
|
||||
return Color.rgb((int)(fcomp[0]*255),(int)(fcomp[1]*255),(int)(fcomp[2]*255));
|
||||
}
|
||||
|
||||
@Override public int toColor(int[] icomp) {
|
||||
return Color.rgb(icomp[0],icomp[1],icomp[2]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get the type of this color space (TYPE_RGB)
|
||||
*/
|
||||
@Override public int getType() {
|
||||
return COLORSPACE_GRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RGB";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -28,187 +28,186 @@ import com.sun.pdfview.PDFPaint;
|
|||
import com.sun.pdfview.PDFParseException;
|
||||
import com.sun.pdfview.function.PDFFunction;
|
||||
|
||||
|
||||
/**
|
||||
* A color space that can convert a set of color components into
|
||||
* PDFPaint.
|
||||
*
|
||||
* @author Mike Wessler
|
||||
*/
|
||||
public abstract class PDFColorSpace {
|
||||
|
||||
/** the name of the device-dependent gray color space */
|
||||
public static final int COLORSPACE_GRAY = 0;
|
||||
|
||||
/** the name of the device-dependent RGB color space */
|
||||
public static final int COLORSPACE_RGB = 1;
|
||||
/** the name of the device-dependent gray color space */
|
||||
public static final int COLORSPACE_GRAY = 0;
|
||||
|
||||
/** the name of the device-dependent CMYK color space */
|
||||
public static final int COLORSPACE_CMYK = 2;
|
||||
/** the name of the device-dependent RGB color space */
|
||||
public static final int COLORSPACE_RGB = 1;
|
||||
|
||||
/** the name of the pattern color space */
|
||||
public static final int COLORSPACE_PATTERN = 3;
|
||||
/** the name of the device-dependent CMYK color space */
|
||||
public static final int COLORSPACE_CMYK = 2;
|
||||
|
||||
public static final int COLORSPACE_INDEXED = 4;
|
||||
/** the name of the pattern color space */
|
||||
public static final int COLORSPACE_PATTERN = 3;
|
||||
|
||||
public static final int COLORSPACE_ALTERNATE = 5;
|
||||
public static final int COLORSPACE_INDEXED = 4;
|
||||
|
||||
/** the device-dependent color spaces */
|
||||
// private static PDFColorSpace graySpace =
|
||||
// new PDFColorSpace(ColorSpace.getInstance(ColorSpace.CS_GRAY));
|
||||
private static PDFColorSpace rgbSpace = new RGBColorSpace();
|
||||
private static PDFColorSpace cmykSpace = new CMYKColorSpace();
|
||||
public static final int COLORSPACE_ALTERNATE = 5;
|
||||
|
||||
/** the pattern space */
|
||||
private static PDFColorSpace patternSpace = new RGBColorSpace(); // TODO [FHe]
|
||||
/** the device-dependent color spaces */
|
||||
// private static PDFColorSpace graySpace =
|
||||
// new PDFColorSpace(ColorSpace.getInstance(ColorSpace.CS_GRAY));
|
||||
private static PDFColorSpace rgbSpace = new RGBColorSpace();
|
||||
private static PDFColorSpace cmykSpace = new CMYKColorSpace();
|
||||
private static PDFColorSpace icciSpace = new ICCIColorSpace();
|
||||
/** the pattern space */
|
||||
private static PDFColorSpace patternSpace = new RGBColorSpace(); // TODO [FHe]
|
||||
|
||||
/** graySpace and the gamma correction for it. */
|
||||
private static PDFColorSpace graySpace = new GrayColorSpace();
|
||||
/** graySpace and the gamma correction for it. */
|
||||
private static PDFColorSpace graySpace = new GrayColorSpace();
|
||||
|
||||
/**
|
||||
* create a PDFColorSpace based on a Java ColorSpace
|
||||
*
|
||||
* @param cs the Java ColorSpace
|
||||
*/
|
||||
protected PDFColorSpace() {
|
||||
}
|
||||
|
||||
/**
|
||||
* create a PDFColorSpace based on a Java ColorSpace
|
||||
* @param cs the Java ColorSpace
|
||||
*/
|
||||
protected PDFColorSpace() {
|
||||
/**
|
||||
* Get a color space by name
|
||||
*
|
||||
* @param name the name of one of the device-dependent color spaces
|
||||
*/
|
||||
public static PDFColorSpace getColorSpace(int name) {
|
||||
switch (name) {
|
||||
case COLORSPACE_GRAY:
|
||||
return graySpace;
|
||||
|
||||
case COLORSPACE_RGB:
|
||||
return rgbSpace;
|
||||
|
||||
case COLORSPACE_CMYK:
|
||||
return cmykSpace;
|
||||
|
||||
case COLORSPACE_PATTERN:
|
||||
return patternSpace;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown Color Space name: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a color space specified in a PDFObject
|
||||
*
|
||||
* @param csobj the PDFObject with the colorspace information
|
||||
*/
|
||||
public static PDFColorSpace getColorSpace(PDFObject csobj, Map resources) throws IOException {
|
||||
String name;
|
||||
|
||||
PDFObject colorSpaces = null;
|
||||
|
||||
if (resources != null) {
|
||||
colorSpaces = (PDFObject) resources.get("ColorSpace");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a color space by name
|
||||
*
|
||||
* @param name the name of one of the device-dependent color spaces
|
||||
*/
|
||||
public static PDFColorSpace getColorSpace(int name) {
|
||||
switch (name) {
|
||||
case COLORSPACE_GRAY:
|
||||
return graySpace;
|
||||
if (csobj.getType() == PDFObject.NAME) {
|
||||
name = csobj.getStringValue();
|
||||
|
||||
case COLORSPACE_RGB:
|
||||
return rgbSpace;
|
||||
|
||||
case COLORSPACE_CMYK:
|
||||
return cmykSpace;
|
||||
|
||||
case COLORSPACE_PATTERN:
|
||||
return patternSpace;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown Color Space name: " +
|
||||
name);
|
||||
}
|
||||
if (name.equals("DeviceGray") || name.equals("G")) {
|
||||
return getColorSpace(COLORSPACE_GRAY);
|
||||
} else if (name.equals("DeviceRGB") || name.equals("RGB")) {
|
||||
return getColorSpace(COLORSPACE_RGB);
|
||||
} else if (name.equals("DeviceCMYK") || name.equals("CMYK")) {
|
||||
return getColorSpace(COLORSPACE_CMYK);
|
||||
} else if (name.equals("Pattern")) {
|
||||
return getColorSpace(COLORSPACE_PATTERN);
|
||||
} else if (colorSpaces != null) {
|
||||
csobj = (PDFObject) colorSpaces.getDictRef(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a color space specified in a PDFObject
|
||||
*
|
||||
* @param csobj the PDFObject with the colorspace information
|
||||
*/
|
||||
public static PDFColorSpace getColorSpace(PDFObject csobj, Map resources)
|
||||
throws IOException {
|
||||
String name;
|
||||
|
||||
PDFObject colorSpaces = null;
|
||||
|
||||
if (resources != null) {
|
||||
colorSpaces = (PDFObject) resources.get("ColorSpace");
|
||||
}
|
||||
|
||||
if (csobj.getType() == PDFObject.NAME) {
|
||||
name = csobj.getStringValue();
|
||||
|
||||
if (name.equals("DeviceGray") || name.equals("G")) {
|
||||
return getColorSpace(COLORSPACE_GRAY);
|
||||
} else if (name.equals("DeviceRGB") || name.equals("RGB")) {
|
||||
return getColorSpace(COLORSPACE_RGB);
|
||||
} else if (name.equals("DeviceCMYK") || name.equals("CMYK")) {
|
||||
return getColorSpace(COLORSPACE_CMYK);
|
||||
} else if (name.equals("Pattern")) {
|
||||
return getColorSpace(COLORSPACE_PATTERN);
|
||||
} else if (colorSpaces != null) {
|
||||
csobj = (PDFObject) colorSpaces.getDictRef(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (csobj == null) {
|
||||
return null;
|
||||
} else if (csobj.getCache() != null) {
|
||||
return (PDFColorSpace) csobj.getCache();
|
||||
}
|
||||
|
||||
PDFColorSpace value = null;
|
||||
|
||||
// csobj is [/name <<dict>>]
|
||||
PDFObject[] ary = csobj.getArray();
|
||||
name = ary[0].getStringValue();
|
||||
|
||||
if (name.equals("CalGray")) {
|
||||
value = graySpace; // TODO [FHe]
|
||||
} else if (name.equals("CalRGB")) {
|
||||
value = rgbSpace; // TODO [FHe]
|
||||
} else if (name.equals("Lab")) {
|
||||
value = rgbSpace; // TODO [FHe]
|
||||
} else if (name.equals("ICCBased")) {
|
||||
value = rgbSpace; // TODO [FHe]
|
||||
} else if (name.equals("Separation") || name.equals("DeviceN")) {
|
||||
PDFColorSpace alternate = getColorSpace(ary[2], resources);
|
||||
PDFFunction function = PDFFunction.getFunction(ary[3]);
|
||||
value = new AlternateColorSpace(alternate, function);
|
||||
} else if (name.equals("Indexed") || name.equals("I")) {
|
||||
/**
|
||||
* 4.5.5 [/Indexed baseColor hival lookup]
|
||||
*/
|
||||
PDFColorSpace refspace = getColorSpace(ary[1], resources);
|
||||
|
||||
// number of indices= ary[2], data is in ary[3];
|
||||
int count = ary[2].getIntValue();
|
||||
value = new IndexedColor(refspace, count, ary[3]);
|
||||
} else if (name.equals("Pattern")) {
|
||||
return rgbSpace; // TODO [FHe]
|
||||
} else {
|
||||
throw new PDFParseException("Unknown color space: " + name +
|
||||
" with " + ary[1]);
|
||||
}
|
||||
|
||||
csobj.setCache(value);
|
||||
|
||||
return value;
|
||||
if (csobj == null) {
|
||||
return null;
|
||||
} else if (csobj.getCache() != null) {
|
||||
return (PDFColorSpace) csobj.getCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* get the number of components expected in the getPaint command
|
||||
*/
|
||||
public abstract int getNumComponents();
|
||||
PDFColorSpace value = null;
|
||||
|
||||
/**
|
||||
* get the PDFPaint representing the color described by the
|
||||
* given color components
|
||||
* @param components the color components corresponding to the given
|
||||
* colorspace
|
||||
* @return a PDFPaint object representing the closest Color to the
|
||||
* given components.
|
||||
*/
|
||||
public PDFPaint getPaint(float[] components) {
|
||||
return PDFPaint.getColorPaint(toColor(components));
|
||||
}
|
||||
public PDFPaint getFillPaint(float[] components) {
|
||||
return PDFPaint.getPaint(toColor(components));
|
||||
// csobj is [/name <<dict>>]
|
||||
PDFObject[] ary = csobj.getArray();
|
||||
name = ary[0].getStringValue();
|
||||
|
||||
if (name.equals("CalGray")) {
|
||||
value = graySpace; // TODO [FHe]
|
||||
} else if (name.equals("CalRGB")) {
|
||||
value = rgbSpace; // TODO [FHe]
|
||||
} else if (name.equals("Lab")) {
|
||||
value = rgbSpace; // TODO [FHe]
|
||||
} else if (name.equals("ICCBased")) {
|
||||
value = rgbSpace;//icciSpace; // TODO [FHe]
|
||||
} else if (name.equals("Separation") || name.equals("DeviceN")) {
|
||||
PDFColorSpace alternate = getColorSpace(ary[2], resources);
|
||||
PDFFunction function = PDFFunction.getFunction(ary[3]);
|
||||
value = new AlternateColorSpace(alternate, function);
|
||||
} else if (name.equals("Indexed") || name.equals("I")) {
|
||||
/**
|
||||
* 4.5.5 [/Indexed baseColor hival lookup]
|
||||
*/
|
||||
PDFColorSpace refspace = getColorSpace(ary[1], resources);
|
||||
|
||||
// number of indices= ary[2], data is in ary[3];
|
||||
int count = ary[2].getIntValue();
|
||||
value = new IndexedColor(refspace, count, ary[3]);
|
||||
} else if (name.equals("Pattern")) {
|
||||
return rgbSpace; // TODO [FHe]
|
||||
} else {
|
||||
throw new PDFParseException("Unknown color space: " + name +
|
||||
" with " + ary[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the type of this color space
|
||||
*/
|
||||
public abstract int getType();
|
||||
/**
|
||||
* get the name of this color space
|
||||
*/
|
||||
public abstract String getName();
|
||||
csobj.setCache(value);
|
||||
|
||||
public abstract int toColor(float[] fcomp);
|
||||
|
||||
public abstract int toColor(int[] icomp);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ColorSpace["+getName()+"]";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the number of components expected in the getPaint command
|
||||
*/
|
||||
public abstract int getNumComponents();
|
||||
|
||||
/**
|
||||
* get the PDFPaint representing the color described by the
|
||||
* given color components
|
||||
*
|
||||
* @param components the color components corresponding to the given
|
||||
* colorspace
|
||||
* @return a PDFPaint object representing the closest Color to the
|
||||
* given components.
|
||||
*/
|
||||
public PDFPaint getPaint(float[] components) {
|
||||
return PDFPaint.getColorPaint(toColor(components));
|
||||
}
|
||||
|
||||
public PDFPaint getFillPaint(float[] components) {
|
||||
return PDFPaint.getPaint(toColor(components));
|
||||
}
|
||||
|
||||
/**
|
||||
* get the type of this color space
|
||||
*/
|
||||
public abstract int getType();
|
||||
|
||||
/**
|
||||
* get the name of this color space
|
||||
*/
|
||||
public abstract String getName();
|
||||
|
||||
public abstract int toColor(float[] fcomp);
|
||||
|
||||
public abstract int toColor(int[] icomp);
|
||||
|
||||
@Override public String toString() {
|
||||
return "ColorSpace[" + getName() + "]";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,19 +21,13 @@
|
|||
|
||||
package com.sun.pdfview.decode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import net.sf.andpdf.nio.ByteBuffer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.util.Log;
|
||||
|
||||
import com.sun.pdfview.PDFObject;
|
||||
import com.sun.pdfview.PDFParseException;
|
||||
import com.sun.pdfview.colorspace.PDFColorSpace;
|
||||
import net.sf.andpdf.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* decode a DCT encoded array into a byte array. This class uses Java's
|
||||
|
|
@ -43,52 +37,49 @@ import com.sun.pdfview.colorspace.PDFColorSpace;
|
|||
*/
|
||||
public class DCTDecode {
|
||||
|
||||
/**
|
||||
* decode an array of bytes in DCT format.
|
||||
* <p>
|
||||
* DCT is the format used by JPEG images, so this class simply
|
||||
* loads the DCT-format bytes as an image, then reads the bytes out
|
||||
* of the image to create the array. Unfortunately, their most
|
||||
* likely use is to get turned BACK into an image, so this isn't
|
||||
* terribly efficient... but is is general... don't hit, please.
|
||||
* <p>
|
||||
* The DCT-encoded stream may have 1, 3 or 4 samples per pixel, depending
|
||||
* on the colorspace of the image. In decoding, we look for the colorspace
|
||||
* in the stream object's dictionary to decide how to decode this image.
|
||||
* If no colorspace is present, we guess 3 samples per pixel.
|
||||
*
|
||||
* @param dict the stream dictionary
|
||||
* @param buf the DCT-encoded buffer
|
||||
* @param params the parameters to the decoder (ignored)
|
||||
* @return the decoded buffer
|
||||
*/
|
||||
protected static ByteBuffer decode(PDFObject dict, ByteBuffer buf,
|
||||
PDFObject params) throws PDFParseException
|
||||
{
|
||||
// System.out.println("DCTDecode image info: "+params);
|
||||
buf.rewind();
|
||||
|
||||
// copy the data into a byte array required by createimage
|
||||
byte[] ary = new byte[buf.remaining()];
|
||||
buf.get(ary);
|
||||
/**
|
||||
* decode an array of bytes in DCT format.
|
||||
* <p>
|
||||
* DCT is the format used by JPEG images, so this class simply
|
||||
* loads the DCT-format bytes as an image, then reads the bytes out
|
||||
* of the image to create the array. Unfortunately, their most
|
||||
* likely use is to get turned BACK into an image, so this isn't
|
||||
* terribly efficient... but is is general... don't hit, please.
|
||||
* <p>
|
||||
* The DCT-encoded stream may have 1, 3 or 4 samples per pixel, depending
|
||||
* on the colorspace of the image. In decoding, we look for the colorspace
|
||||
* in the stream object's dictionary to decide how to decode this image.
|
||||
* If no colorspace is present, we guess 3 samples per pixel.
|
||||
*
|
||||
* @param dict the stream dictionary
|
||||
* @param buf the DCT-encoded buffer
|
||||
* @param params the parameters to the decoder (ignored)
|
||||
* @return the decoded buffer
|
||||
*/
|
||||
protected static ByteBuffer decode(PDFObject dict, ByteBuffer buf, PDFObject params) throws PDFParseException {
|
||||
// System.out.println("DCTDecode image info: "+params);
|
||||
buf.rewind();
|
||||
|
||||
Bitmap img = BitmapFactory.decodeByteArray(ary, 0, ary.length);
|
||||
// copy the data into a byte array required by createimage
|
||||
byte[] ary = new byte[buf.remaining()];
|
||||
buf.get(ary);
|
||||
Bitmap img = BitmapFactory.decodeByteArray(ary, 0, ary.length);
|
||||
|
||||
if (img == null)
|
||||
throw new PDFParseException("could not decode image of compressed size "+ary.length);
|
||||
Config conf = img.getConfig();
|
||||
Log.e("ANDPDF.dctdecode", "decoded image type"+conf);
|
||||
int size = 4*img.getWidth()*img.getHeight();
|
||||
if (conf == Config.RGB_565)
|
||||
size = 2*img.getWidth()*img.getHeight();
|
||||
// TODO [FHe]: else ... what do we get for gray? Config.ALPHA_8?
|
||||
|
||||
java.nio.ByteBuffer byteBuf = java.nio.ByteBuffer.allocate(size);
|
||||
img.copyPixelsToBuffer(byteBuf);
|
||||
|
||||
ByteBuffer result = ByteBuffer.fromNIO(byteBuf);
|
||||
result.rewind();
|
||||
return result;
|
||||
if (img == null) throw new PDFParseException("could not decode image of compressed size " + ary.length);
|
||||
Config conf = img.getConfig();
|
||||
Log.e("ANDPDF.dctdecode", "decoded image type" + conf);
|
||||
int size = 4 * img.getWidth() * img.getHeight();
|
||||
if (conf == Config.RGB_565) {
|
||||
size = 2 * img.getWidth() * img.getHeight();
|
||||
}
|
||||
// TODO [FHe]: else ... what do we get for gray? Config.ALPHA_8?
|
||||
|
||||
java.nio.ByteBuffer byteBuf = java.nio.ByteBuffer.allocate(size);
|
||||
img.copyPixelsToBuffer(byteBuf);
|
||||
ByteBuffer result = ByteBuffer.fromNIO(byteBuf);
|
||||
result.rewind();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,6 +111,9 @@ public class HeadTable extends TrueTypeTable {
|
|||
* Parse the data before it is set
|
||||
*/
|
||||
public void setData(ByteBuffer data) {
|
||||
if (data.remaining() != 54) {
|
||||
throw new IllegalArgumentException("Bad Head table size");
|
||||
}
|
||||
setVersion(data.getInt());
|
||||
setFontRevision(data.getInt());
|
||||
setChecksumAdjustment(data.getInt());
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
package net.sf.andpdf.pdfviewer;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import uk.co.senab.photoview.PhotoViewAttacher;
|
||||
|
||||
|
||||
public abstract class OnSwipeTouchListener implements PhotoViewAttacher.OnSingleFlingListener {
|
||||
|
||||
private static final int SWIPE_DISTANCE_THRESHOLD = 100;
|
||||
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
|
||||
|
||||
@Override
|
||||
public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) {
|
||||
float distanceX = e2.getX() - e1.getX();
|
||||
float distanceY = e2.getY() - e1.getY();
|
||||
if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
|
||||
if (distanceX > 0)
|
||||
onSwipeRight();
|
||||
else
|
||||
onSwipeLeft();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void onSwipeLeft();
|
||||
|
||||
public abstract void onSwipeRight();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,639 @@
|
|||
package net.sf.andpdf.pdfviewer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.sun.pdfview.PDFFile;
|
||||
import com.sun.pdfview.PDFImage;
|
||||
import com.sun.pdfview.PDFPage;
|
||||
import com.sun.pdfview.PDFPaint;
|
||||
import com.sun.pdfview.decrypt.PDFAuthenticationFailureException;
|
||||
import com.sun.pdfview.decrypt.PDFPassword;
|
||||
import com.sun.pdfview.font.PDFFont;
|
||||
|
||||
import net.sf.andpdf.nio.ByteBuffer;
|
||||
import net.sf.andpdf.pdfviewer.gui.FullScrollView;
|
||||
import net.sf.andpdf.refs.HardReference;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* U:\Android\android-sdk-windows-1.5_r1\tools\adb push u:\Android\simple_T.pdf /data/test.pdf
|
||||
*
|
||||
* @author ferenc.hechler
|
||||
*/
|
||||
public class PdfViewerActivity extends Activity {
|
||||
|
||||
private static final int STARTPAGE = 1;
|
||||
private static final float STARTZOOM = 1.0f;
|
||||
|
||||
private static final float MIN_ZOOM = 0.25f;
|
||||
private static final float MAX_ZOOM = 3.0f;
|
||||
private static final float ZOOM_INCREMENT = 1.5f;
|
||||
|
||||
private static final String TAG = "PDFVIEWER";
|
||||
|
||||
public static final String EXTRA_SHOWIMAGES = "net.sf.andpdf.extra.SHOWIMAGES";
|
||||
public static final String EXTRA_ANTIALIAS = "net.sf.andpdf.extra.ANTIALIAS";
|
||||
public static final String EXTRA_USEFONTSUBSTITUTION = "net.sf.andpdf.extra.USEFONTSUBSTITUTION";
|
||||
|
||||
public static final boolean DEFAULTSHOWIMAGES = true;
|
||||
public static final boolean DEFAULTANTIALIAS = true;
|
||||
public static final boolean DEFAULTUSEFONTSUBSTITUTION = false;
|
||||
|
||||
private final static int MENU_NEXT_PAGE = 1;
|
||||
private final static int MENU_PREV_PAGE = 2;
|
||||
private final static int MENU_GOTO_PAGE = 3;
|
||||
private final static int MENU_ZOOM_IN = 4;
|
||||
private final static int MENU_ZOOM_OUT = 5;
|
||||
private final static int MENU_BACK = 6;
|
||||
private final static int MENU_CLEANUP = 7;
|
||||
|
||||
private final static int DIALOG_PAGENUM = 1;
|
||||
|
||||
private GraphView mOldGraphView;
|
||||
private GraphView mGraphView;
|
||||
private PDFFile mPdfFile;
|
||||
public static byte[] byteArray;
|
||||
private int mPage;
|
||||
private float mZoom;
|
||||
private ProgressDialog progress;
|
||||
|
||||
private PDFPage mPdfPage;
|
||||
|
||||
private Thread backgroundThread;
|
||||
private Handler uiHandler;
|
||||
|
||||
/**
|
||||
* restore member variables from previously saved instance
|
||||
*
|
||||
* @return true if instance to restore from was found
|
||||
* @see
|
||||
*/
|
||||
private boolean restoreInstance() {
|
||||
mOldGraphView = null;
|
||||
Log.e(TAG, "restoreInstance");
|
||||
if (getLastNonConfigurationInstance() == null) return false;
|
||||
PdfViewerActivity inst = (PdfViewerActivity) getLastNonConfigurationInstance();
|
||||
if (inst != this) {
|
||||
Log.e(TAG, "restoring Instance");
|
||||
mOldGraphView = inst.mGraphView;
|
||||
mPage = inst.mPage;
|
||||
mPdfFile = inst.mPdfFile;
|
||||
mPdfPage = inst.mPdfPage;
|
||||
mZoom = inst.mZoom;
|
||||
backgroundThread = inst.backgroundThread;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Log.i(TAG, "onCreate");
|
||||
uiHandler = new Handler();
|
||||
restoreInstance();
|
||||
|
||||
if (mOldGraphView != null) {
|
||||
mGraphView = new GraphView(this);
|
||||
mGraphView.mBi = mOldGraphView.mBi;
|
||||
mOldGraphView = null;
|
||||
mGraphView.pdfView.setImageBitmap(mGraphView.mBi);
|
||||
mGraphView.updateTexts();
|
||||
setContentView(mGraphView);
|
||||
} else {
|
||||
mGraphView = new GraphView(this);
|
||||
Intent intent = getIntent();
|
||||
Log.i(TAG, "" + intent);
|
||||
|
||||
PDFImage.sShowImages = getIntent().getBooleanExtra(PdfViewerActivity.EXTRA_SHOWIMAGES, PdfViewerActivity.DEFAULTSHOWIMAGES);
|
||||
PDFPaint.s_doAntiAlias = getIntent().getBooleanExtra(PdfViewerActivity.EXTRA_ANTIALIAS, PdfViewerActivity.DEFAULTANTIALIAS);
|
||||
PDFFont.sUseFontSubstitution = getIntent().getBooleanExtra(PdfViewerActivity.EXTRA_USEFONTSUBSTITUTION,
|
||||
PdfViewerActivity.DEFAULTUSEFONTSUBSTITUTION);
|
||||
HardReference.sKeepCaches = true;
|
||||
|
||||
mPage = STARTPAGE;
|
||||
mZoom = STARTZOOM;
|
||||
|
||||
setContent(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void setContent(String password) {
|
||||
try {
|
||||
openFile(byteArray, password);
|
||||
setContentView(mGraphView);
|
||||
startRenderThread(mPage, mZoom);
|
||||
} catch (PDFAuthenticationFailureException e) {
|
||||
setContentView(getPdfPasswordLayoutResource());
|
||||
final EditText etPW = (EditText) findViewById(getPdfPasswordEditField());
|
||||
Button btOK = (Button) findViewById(getPdfPasswordOkButton());
|
||||
Button btExit = (Button) findViewById(getPdfPasswordExitButton());
|
||||
btOK.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
String pw = etPW.getText().toString();
|
||||
setContent(pw);
|
||||
}
|
||||
});
|
||||
btExit.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "an unexpected exception occurred");
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void startRenderThread(final int page, final float zoom) {
|
||||
if (backgroundThread != null) return;
|
||||
backgroundThread = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
if (mPdfFile != null) {
|
||||
showPage(page, zoom);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
}
|
||||
backgroundThread = null;
|
||||
}
|
||||
});
|
||||
updateImageStatus();
|
||||
backgroundThread.start();
|
||||
}
|
||||
|
||||
private void updateImageStatus() {
|
||||
if (backgroundThread == null) {
|
||||
mGraphView.updateUi();
|
||||
return;
|
||||
}
|
||||
mGraphView.updateUi();
|
||||
mGraphView.postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
updateImageStatus();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
menu.add(Menu.NONE, MENU_PREV_PAGE, Menu.NONE, "Previous Page").setIcon(getPreviousPageImageResource());
|
||||
menu.add(Menu.NONE, MENU_NEXT_PAGE, Menu.NONE, "Next Page").setIcon(getNextPageImageResource());
|
||||
menu.add(Menu.NONE, MENU_GOTO_PAGE, Menu.NONE, "Goto Page");
|
||||
menu.add(Menu.NONE, MENU_ZOOM_OUT, Menu.NONE, "Zoom Out").setIcon(getZoomOutImageResource());
|
||||
menu.add(Menu.NONE, MENU_ZOOM_IN, Menu.NONE, "Zoom In").setIcon(getZoomInImageResource());
|
||||
if (HardReference.sKeepCaches) menu.add(Menu.NONE, MENU_CLEANUP, Menu.NONE, "Clear Caches");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a menu item is selected.
|
||||
*/
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
switch (item.getItemId()) {
|
||||
case MENU_NEXT_PAGE: {
|
||||
nextPage();
|
||||
break;
|
||||
}
|
||||
case MENU_PREV_PAGE: {
|
||||
prevPage();
|
||||
break;
|
||||
}
|
||||
case MENU_GOTO_PAGE: {
|
||||
gotoPage();
|
||||
break;
|
||||
}
|
||||
case MENU_ZOOM_IN: {
|
||||
zoomIn();
|
||||
break;
|
||||
}
|
||||
case MENU_ZOOM_OUT: {
|
||||
zoomOut();
|
||||
break;
|
||||
}
|
||||
case MENU_BACK: {
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
case MENU_CLEANUP: {
|
||||
HardReference.cleanup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void zoomIn() {
|
||||
if (mPdfFile != null) {
|
||||
if (mZoom < MAX_ZOOM) {
|
||||
mZoom *= ZOOM_INCREMENT;
|
||||
if (mZoom > MAX_ZOOM) mZoom = MAX_ZOOM;
|
||||
|
||||
if (mZoom >= MAX_ZOOM) {
|
||||
Log.d(TAG, "Disabling zoom in button");
|
||||
mGraphView.bZoomIn.setEnabled(false);
|
||||
} else {
|
||||
mGraphView.bZoomIn.setEnabled(true);
|
||||
}
|
||||
|
||||
mGraphView.bZoomOut.setEnabled(true);
|
||||
|
||||
startRenderThread(mPage, mZoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void zoomOut() {
|
||||
if (mPdfFile != null) {
|
||||
if (mZoom > MIN_ZOOM) {
|
||||
mZoom /= ZOOM_INCREMENT;
|
||||
if (mZoom < MIN_ZOOM) mZoom = MIN_ZOOM;
|
||||
|
||||
if (mZoom <= MIN_ZOOM) {
|
||||
Log.d(TAG, "Disabling zoom out button");
|
||||
mGraphView.bZoomOut.setEnabled(false);
|
||||
} else {
|
||||
mGraphView.bZoomOut.setEnabled(true);
|
||||
}
|
||||
|
||||
mGraphView.bZoomIn.setEnabled(true);
|
||||
|
||||
startRenderThread(mPage, mZoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void nextPage() {
|
||||
if (mPdfFile != null) {
|
||||
if (mPage < mPdfFile.getNumPages()) {
|
||||
mPage += 1;
|
||||
mGraphView.bZoomOut.setEnabled(true);
|
||||
mGraphView.bZoomIn.setEnabled(true);
|
||||
progress = ProgressDialog.show(PdfViewerActivity.this, "Loading", "Loading PDF Page " + mPage, true, true);
|
||||
startRenderThread(mPage, mZoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void prevPage() {
|
||||
if (mPdfFile != null) {
|
||||
if (mPage > 1) {
|
||||
mPage -= 1;
|
||||
mGraphView.bZoomOut.setEnabled(true);
|
||||
mGraphView.bZoomIn.setEnabled(true);
|
||||
progress = ProgressDialog.show(PdfViewerActivity.this, "Loading", "Loading PDF Page " + mPage, true, true);
|
||||
startRenderThread(mPage, mZoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void gotoPage() {
|
||||
if (mPdfFile != null) {
|
||||
showDialog(DIALOG_PAGENUM);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case DIALOG_PAGENUM:
|
||||
LayoutInflater factory = LayoutInflater.from(this);
|
||||
final View pagenumView = factory.inflate(getPdfPageNumberResource(), null);
|
||||
final EditText edPagenum = (EditText) pagenumView.findViewById(getPdfPageNumberEditField());
|
||||
edPagenum.setText(Integer.toString(mPage));
|
||||
edPagenum.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (event == null || (event.getAction() == 1)) {
|
||||
// Hide the keyboard
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(edPagenum.getWindowToken(), 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return new AlertDialog.Builder(this)
|
||||
//.setIcon(R.drawable.icon)
|
||||
.setTitle("Jump to page")
|
||||
.setView(pagenumView)
|
||||
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
String strPagenum = edPagenum.getText().toString();
|
||||
int pageNum = mPage;
|
||||
try {
|
||||
pageNum = Integer.parseInt(strPagenum);
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
if ((pageNum != mPage) && (pageNum >= 1) && (pageNum <= mPdfFile.getNumPages())) {
|
||||
mPage = pageNum;
|
||||
mGraphView.bZoomOut.setEnabled(true);
|
||||
mGraphView.bZoomIn.setEnabled(true);
|
||||
progress =
|
||||
ProgressDialog.show(PdfViewerActivity.this, "Loading", "Loading PDF Page " + mPage, true, true);
|
||||
startRenderThread(mPage, mZoom);
|
||||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
}
|
||||
})
|
||||
.create();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private class GraphView extends FullScrollView {
|
||||
public Bitmap mBi;
|
||||
public ImageView pdfView;
|
||||
public Button mBtPage;
|
||||
private Button mBtPage2;
|
||||
|
||||
ImageButton bZoomOut;
|
||||
ImageButton bZoomIn;
|
||||
|
||||
public GraphView(Context context) {
|
||||
super(context);
|
||||
|
||||
// LinearLayout.LayoutParams matchLp =
|
||||
// new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
|
||||
LinearLayout.LayoutParams lpWrap1 = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 1);
|
||||
LinearLayout.LayoutParams lpWrap10 = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 10);
|
||||
|
||||
LinearLayout vl = new LinearLayout(context);
|
||||
vl.setLayoutParams(lpWrap10);
|
||||
vl.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
if (mOldGraphView == null) {
|
||||
progress = ProgressDialog.show(PdfViewerActivity.this, "Loading", "Loading PDF Page", true, true);
|
||||
}
|
||||
|
||||
addNavButtons(vl);
|
||||
// remember page button for updates
|
||||
mBtPage2 = mBtPage;
|
||||
|
||||
pdfView = new ImageView(context);
|
||||
setPageBitmap(null);
|
||||
updateImage();
|
||||
pdfView.setLayoutParams(lpWrap1);
|
||||
pdfView.setPadding(5, 5, 5, 5);
|
||||
vl.addView(pdfView);
|
||||
|
||||
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 100));
|
||||
setBackgroundColor(Color.LTGRAY);
|
||||
setHorizontalScrollBarEnabled(true);
|
||||
setHorizontalFadingEdgeEnabled(true);
|
||||
setVerticalScrollBarEnabled(true);
|
||||
setVerticalFadingEdgeEnabled(true);
|
||||
addView(vl);
|
||||
}
|
||||
|
||||
private void addNavButtons(ViewGroup vg) {
|
||||
|
||||
LinearLayout.LayoutParams lpChild1 = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 1);
|
||||
LinearLayout.LayoutParams lpWrap10 = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 10);
|
||||
|
||||
Context context = vg.getContext();
|
||||
LinearLayout hl = new LinearLayout(context);
|
||||
hl.setLayoutParams(lpWrap10);
|
||||
hl.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
// zoom out button
|
||||
bZoomOut = new ImageButton(context);
|
||||
bZoomOut.setBackgroundDrawable(null);
|
||||
bZoomOut.setLayoutParams(lpChild1);
|
||||
bZoomOut.setImageResource(getZoomOutImageResource());
|
||||
bZoomOut.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
zoomOut();
|
||||
}
|
||||
});
|
||||
hl.addView(bZoomOut);
|
||||
|
||||
// zoom in button
|
||||
bZoomIn = new ImageButton(context);
|
||||
bZoomIn.setBackgroundDrawable(null);
|
||||
bZoomIn.setLayoutParams(lpChild1);
|
||||
bZoomIn.setImageResource(getZoomInImageResource());
|
||||
bZoomIn.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
zoomIn();
|
||||
}
|
||||
});
|
||||
hl.addView(bZoomIn);
|
||||
|
||||
// prev button
|
||||
ImageButton bPrev = new ImageButton(context);
|
||||
bPrev.setBackgroundDrawable(null);
|
||||
bPrev.setLayoutParams(lpChild1);
|
||||
//bPrev.setText("<");
|
||||
//bPrev.setWidth(40);
|
||||
bPrev.setImageResource(getPreviousPageImageResource());
|
||||
bPrev.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
prevPage();
|
||||
}
|
||||
});
|
||||
hl.addView(bPrev);
|
||||
|
||||
// page button
|
||||
mBtPage = new Button(context);
|
||||
mBtPage.setLayoutParams(lpChild1);
|
||||
|
||||
mBtPage.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
gotoPage();
|
||||
}
|
||||
});
|
||||
hl.addView(mBtPage);
|
||||
|
||||
// next button
|
||||
ImageButton bNext = new ImageButton(context);
|
||||
bNext.setBackgroundDrawable(null);
|
||||
bNext.setLayoutParams(lpChild1);
|
||||
bNext.setImageResource(getNextPageImageResource());
|
||||
bNext.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
nextPage();
|
||||
}
|
||||
});
|
||||
hl.addView(bNext);
|
||||
|
||||
vg.addView(hl);
|
||||
|
||||
addSpace(vg, 6, 6);
|
||||
}
|
||||
|
||||
private void addSpace(ViewGroup vg, int width, int height) {
|
||||
TextView tvSpacer = new TextView(vg.getContext());
|
||||
tvSpacer.setLayoutParams(new LinearLayout.LayoutParams(width, height, 1));
|
||||
tvSpacer.setText(null);
|
||||
vg.addView(tvSpacer);
|
||||
}
|
||||
|
||||
private void updateImage() {
|
||||
uiHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
pdfView.setImageBitmap(mBi);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateUi() {
|
||||
uiHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
updateTexts();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setPageBitmap(Bitmap bi) {
|
||||
if (bi != null) {
|
||||
mBi = bi;
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateTexts() {
|
||||
if (mPdfPage != null) {
|
||||
if (mBtPage != null)
|
||||
mBtPage.setText(mPdfPage.getPageNumber() + "/" + mPdfFile.getNumPages());
|
||||
if (mBtPage2 != null)
|
||||
mBtPage2.setText(mPdfPage.getPageNumber() + "/" + mPdfFile.getNumPages());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void showPage(int page, float zoom) throws Exception {
|
||||
try {
|
||||
|
||||
// free memory from previous page
|
||||
mGraphView.setPageBitmap(null);
|
||||
mGraphView.updateImage();
|
||||
|
||||
// Only load the page if it's a different page (i.e. not just changing the zoom level)
|
||||
if (mPdfPage == null || mPdfPage.getPageNumber() != page) {
|
||||
mPdfPage = mPdfFile.getPage(page, true);
|
||||
}
|
||||
|
||||
float width = mPdfPage.getWidth();
|
||||
float height = mPdfPage.getHeight();
|
||||
RectF clip = null;
|
||||
Bitmap bi = mPdfPage.getImage((int) (width * zoom), (int) (height * zoom), clip, true, true);
|
||||
mGraphView.setPageBitmap(bi);
|
||||
mGraphView.updateImage();
|
||||
|
||||
if (progress != null) progress.dismiss();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Open a specific pdf file. Creates a DocumentInfo from the file,
|
||||
* and opens that.</p>
|
||||
* <p/>
|
||||
* <p><b>Note:</b> Mapping the file locks the file until the PDFFile
|
||||
* is closed.</p>
|
||||
*
|
||||
* @param byteArray the file to open
|
||||
* @throws IOException
|
||||
*/
|
||||
public void openFile(final byte[] byteArray, String password) throws IOException {
|
||||
if (byteArray != null) {
|
||||
// now memory-map a byte-buffer
|
||||
ByteBuffer bb = ByteBuffer.NEW(byteArray);
|
||||
// create a PDFFile from the data
|
||||
if (password == null) {
|
||||
mPdfFile = new PDFFile(bb);
|
||||
} else {
|
||||
mPdfFile = new PDFFile(bb, new PDFPassword(password));
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "The error occurred", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
byteArray = null;
|
||||
}
|
||||
|
||||
private int getPreviousPageImageResource() {
|
||||
return R.drawable.left_arrow;
|
||||
}
|
||||
|
||||
private int getNextPageImageResource() {
|
||||
return R.drawable.right_arrow;
|
||||
}
|
||||
|
||||
private int getZoomInImageResource() {
|
||||
return R.drawable.zoom_in;
|
||||
}
|
||||
|
||||
private int getZoomOutImageResource() {
|
||||
return R.drawable.zoom_out;
|
||||
}
|
||||
|
||||
private int getPdfPasswordLayoutResource() {
|
||||
return R.layout.pdf_file_password;
|
||||
}
|
||||
|
||||
private int getPdfPageNumberResource() {
|
||||
return R.layout.dialog_pagenumber;
|
||||
}
|
||||
|
||||
private int getPdfPasswordEditField() {
|
||||
return R.id.etPassword;
|
||||
}
|
||||
|
||||
private int getPdfPasswordOkButton() {
|
||||
return R.id.btOK;
|
||||
}
|
||||
|
||||
private int getPdfPasswordExitButton() {
|
||||
return R.id.btExit;
|
||||
}
|
||||
|
||||
private int getPdfPageNumberEditField() {
|
||||
return R.id.pagenum_edit;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,435 +0,0 @@
|
|||
package net.sf.andpdf.pdfviewer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sun.pdfview.PDFFile;
|
||||
import com.sun.pdfview.PDFImage;
|
||||
import com.sun.pdfview.PDFPage;
|
||||
import com.sun.pdfview.PDFPaint;
|
||||
import com.sun.pdfview.decrypt.PDFAuthenticationFailureException;
|
||||
import com.sun.pdfview.decrypt.PDFPassword;
|
||||
import com.sun.pdfview.font.PDFFont;
|
||||
|
||||
import net.sf.andpdf.nio.ByteBuffer;
|
||||
import net.sf.andpdf.refs.HardReference;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import uk.co.senab.photoview.PhotoViewAttacher;
|
||||
|
||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||
|
||||
/**
|
||||
* U:\Android\android-sdk-windows-1.5_r1\tools\adb push u:\Android\simple_T.pdf /data/test.pdf
|
||||
*
|
||||
* @author ferenc.hechler
|
||||
*/
|
||||
public class PdfViewerFragment extends Fragment {
|
||||
|
||||
private static final int STARTPAGE = 1;
|
||||
|
||||
private static final String TAG = "PDFVIEWER";
|
||||
|
||||
public static final boolean DEFAULTSHOWIMAGES = true;
|
||||
public static final boolean DEFAULTANTIALIAS = true;
|
||||
public static final boolean DEFAULTUSEFONTSUBSTITUTION = false;
|
||||
|
||||
private GraphView mGraphView;
|
||||
private PDFFile mPdfFile;
|
||||
public static byte[] byteArray;
|
||||
private int mPage;
|
||||
private ProgressDialog progress;
|
||||
private TextView pageNumbersView;
|
||||
|
||||
private PDFPage mPdfPage;
|
||||
|
||||
private Thread backgroundThread;
|
||||
private Handler uiHandler;
|
||||
|
||||
private boolean passwordNeeded;
|
||||
private String password;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
|
||||
uiHandler = new Handler();
|
||||
|
||||
progress = ProgressDialog.show(getActivity(), "Loading", "Loading PDF Page", true, true);
|
||||
|
||||
mGraphView = new GraphView(getActivity());
|
||||
PDFImage.sShowImages = PdfViewerFragment.DEFAULTSHOWIMAGES;
|
||||
PDFPaint.s_doAntiAlias = PdfViewerFragment.DEFAULTANTIALIAS;
|
||||
PDFFont.sUseFontSubstitution = PdfViewerFragment.DEFAULTUSEFONTSUBSTITUTION;
|
||||
HardReference.sKeepCaches = true;
|
||||
|
||||
mPage = STARTPAGE;
|
||||
|
||||
final Toolbar toolbar = (Toolbar) LayoutInflater.from(getActivity()).inflate(R.layout.pfd_toolbar, null);
|
||||
toolbar.setContentInsetsAbsolute(0, 0);
|
||||
toolbar.findViewById(R.id.pdf_toolbar_close_image).setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
getFragmentManager().popBackStack();
|
||||
}
|
||||
});
|
||||
|
||||
replaceView(((ActionBarHidden) getActivity()).getCurrentActivityToolbarReplacedBy(toolbar), toolbar);
|
||||
pageNumbersView = (TextView) toolbar.findViewById(R.id.pdf_toolbar_page_numbers_text_view);
|
||||
return setContent(password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (view == null) {
|
||||
getFragmentManager().popBackStack();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!passwordNeeded) {
|
||||
startRenderThread(mPage);
|
||||
} else {
|
||||
hideProgressBar();
|
||||
final EditText etPW = (EditText) view.findViewById(getPdfPasswordEditField());
|
||||
Button btOK = (Button) view.findViewById(getPdfPasswordOkButton());
|
||||
Button btExit = (Button) view.findViewById(getPdfPasswordExitButton());
|
||||
btOK.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
password = etPW.getText().toString();
|
||||
getFragmentManager()
|
||||
.beginTransaction()
|
||||
.detach(PdfViewerFragment.this)
|
||||
.attach(PdfViewerFragment.this)
|
||||
.commit();
|
||||
hideSoftInput();
|
||||
}
|
||||
});
|
||||
btExit.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
hideSoftInput();
|
||||
getFragmentManager().popBackStack();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private View setContent(@Nullable final String password) {
|
||||
try {
|
||||
openFile(byteArray, password);
|
||||
passwordNeeded = false;
|
||||
return mGraphView;
|
||||
} catch (PDFAuthenticationFailureException e) {
|
||||
passwordNeeded = true;
|
||||
return LayoutInflater.from(getActivity()).inflate(R.layout.pdf_file_password, null);
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "an unexpected exception occurred");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private synchronized void startRenderThread(final int page) {
|
||||
if (backgroundThread != null) return;
|
||||
backgroundThread = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
if (mPdfFile != null) {
|
||||
showPage(page);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
}
|
||||
backgroundThread = null;
|
||||
}
|
||||
});
|
||||
updateImageStatus();
|
||||
backgroundThread.start();
|
||||
}
|
||||
|
||||
private void updateImageStatus() {
|
||||
if (backgroundThread == null) {
|
||||
return;
|
||||
}
|
||||
mGraphView.postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
updateImageStatus();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private void nextPage() {
|
||||
if (mPdfFile != null) {
|
||||
if (mPage < mPdfFile.getNumPages()) {
|
||||
mPage += 1;
|
||||
updatePageNumbersView();
|
||||
progress = ProgressDialog.show(getActivity(), "Loading", "Loading PDF Page " + mPage, true, true);
|
||||
startRenderThread(mPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void prevPage() {
|
||||
if (mPdfFile != null) {
|
||||
if (mPage > 1) {
|
||||
mPage -= 1;
|
||||
updatePageNumbersView();
|
||||
progress = ProgressDialog.show(getActivity(), "Loading", "Loading PDF Page " + mPage, true, true);
|
||||
startRenderThread(mPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePageNumbersView() {
|
||||
if (mPdfPage != null) {
|
||||
if (mPdfPage.getPageNumber() == mPdfFile.getNumPages() && mPdfPage.getPageNumber() == 1) {
|
||||
pageNumbersView.setVisibility(View.GONE);
|
||||
} else {
|
||||
pageNumbersView.setText(mPdfPage.getPageNumber() + "/" + mPdfFile.getNumPages());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides device keyboard that is showing over {@link Activity}.
|
||||
* Do NOT use it if keyboard is over {@link android.app.Dialog} - it won't work as they have different {@link Activity#getWindow()}.
|
||||
*/
|
||||
public void hideSoftInput() {
|
||||
if (getActivity().getCurrentFocus() == null) {
|
||||
return;
|
||||
}
|
||||
final InputMethodManager inputManager = (InputMethodManager) getActivity().getSystemService(INPUT_METHOD_SERVICE);
|
||||
inputManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(), 0);
|
||||
getActivity().getWindow().getDecorView().requestFocus();
|
||||
}
|
||||
|
||||
private class GraphView extends FrameLayout {
|
||||
public Bitmap mBi;
|
||||
public ImageView pdfZoomedImageView;
|
||||
public PhotoViewAttacher photoViewAttacher;
|
||||
|
||||
public GraphView(Context context) {
|
||||
super(context);
|
||||
|
||||
final FrameLayout.LayoutParams frameLayout = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
frameLayout.gravity = Gravity.CENTER;
|
||||
pdfZoomedImageView = new ImageView(context);
|
||||
pdfZoomedImageView.setLayoutParams(frameLayout);
|
||||
pdfZoomedImageView.setBackgroundColor(getContext().getResources().getColor(R.color.zoomed_image_view_background));
|
||||
|
||||
photoViewAttacher = new PhotoViewAttacher(pdfZoomedImageView);
|
||||
|
||||
photoViewAttacher.setOnSingleFlingListener(new OnSwipeTouchListener() {
|
||||
|
||||
@Override
|
||||
public void onSwipeLeft() {
|
||||
nextPage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeRight() {
|
||||
prevPage();
|
||||
}
|
||||
|
||||
});
|
||||
setPageBitmap(null);
|
||||
//updateImage();
|
||||
|
||||
final LinearLayout.LayoutParams linearLayout = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
setLayoutParams(linearLayout);
|
||||
addView(pdfZoomedImageView);
|
||||
}
|
||||
|
||||
private void updateImage() {
|
||||
uiHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
pdfZoomedImageView.setImageBitmap(mBi);
|
||||
photoViewAttacher.update();
|
||||
updatePageNumbersView();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setPageBitmap(Bitmap bi) {
|
||||
if (bi != null) {
|
||||
mBi = bi;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: refactor
|
||||
private void showPage(final int page) {
|
||||
// on some Android getWidth() and getHeight() returns 0, so we need to wait until UI is ready
|
||||
try {
|
||||
// free memory from previous page
|
||||
mGraphView.setPageBitmap(null);
|
||||
mGraphView.updateImage();
|
||||
|
||||
// Only load the page if it's a different page (i.e. not just changing the zoom level)
|
||||
if (mPdfPage == null || mPdfPage.getPageNumber() != page) {
|
||||
mPdfPage = mPdfFile.getPage(page, true);
|
||||
}
|
||||
|
||||
final int scale = 3;
|
||||
|
||||
double width = mPdfPage.getWidth() * scale;
|
||||
double height = mPdfPage.getHeight() * scale;
|
||||
|
||||
int maxWidthToPopulate = mGraphView.getWidth();
|
||||
int maxHeightToPopulate = mGraphView.getHeight();
|
||||
|
||||
if (maxWidthToPopulate == 0 || maxHeightToPopulate == 0) {
|
||||
mGraphView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
showPage(page);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
int calculatedWidth;
|
||||
int calculatedHeight;
|
||||
final double widthRatio = width / maxWidthToPopulate;
|
||||
final double heightRatio = height / maxHeightToPopulate;
|
||||
if (width < maxWidthToPopulate && height < maxHeightToPopulate) {
|
||||
if (widthRatio > heightRatio) {
|
||||
calculatedWidth = (int) (width / widthRatio);
|
||||
calculatedHeight = (int) (height / widthRatio);
|
||||
} else {
|
||||
calculatedWidth = (int) (width / heightRatio);
|
||||
calculatedHeight = (int) (height / heightRatio);
|
||||
}
|
||||
} else {
|
||||
if (widthRatio > 1 && heightRatio > 1) {
|
||||
if (widthRatio > heightRatio) {
|
||||
calculatedHeight = (int) (height / widthRatio);
|
||||
calculatedWidth = (int) (width / widthRatio);
|
||||
} else {
|
||||
calculatedHeight = (int) (height / heightRatio);
|
||||
calculatedWidth = (int) (width / heightRatio);
|
||||
}
|
||||
} else {
|
||||
if (widthRatio > heightRatio) {
|
||||
calculatedHeight = (int) (height / widthRatio);
|
||||
calculatedWidth = (int) (width / widthRatio);
|
||||
} else {
|
||||
calculatedHeight = (int) (height / heightRatio);
|
||||
calculatedWidth = (int) (width / heightRatio);
|
||||
}
|
||||
}
|
||||
}
|
||||
final Bitmap bitmap = mPdfPage.getImage(calculatedWidth, calculatedHeight, null, true, true);
|
||||
mGraphView.setPageBitmap(bitmap);
|
||||
mGraphView.updateImage();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
hideProgressBar();
|
||||
}
|
||||
|
||||
private void hideProgressBar() {
|
||||
if (progress != null) {
|
||||
progress.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Open a specific pdf file. Creates a DocumentInfo from the file,
|
||||
* and opens that.</p>
|
||||
* <p/>
|
||||
* <p><b>Note:</b> Mapping the file locks the file until the PDFFile
|
||||
* is closed.</p>
|
||||
*
|
||||
* @param byteArray the file to open
|
||||
* @throws IOException
|
||||
*/
|
||||
public void openFile(final byte[] byteArray, String password) throws IOException {
|
||||
if (byteArray != null) {
|
||||
// now memory-map a byte-buffer
|
||||
ByteBuffer bb = ByteBuffer.NEW(byteArray);
|
||||
// create a PDFFile from the data
|
||||
if (password == null) {
|
||||
mPdfFile = new PDFFile(bb);
|
||||
} else {
|
||||
mPdfFile = new PDFFile(bb, new PDFPassword(password));
|
||||
}
|
||||
} else {
|
||||
hideProgressBar();
|
||||
getFragmentManager().popBackStack();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
byteArray = null;
|
||||
if (mGraphView != null) {
|
||||
mGraphView.mBi = null;
|
||||
mGraphView.photoViewAttacher.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
public static ViewGroup getParent(View view) {
|
||||
return (ViewGroup) view.getParent();
|
||||
}
|
||||
|
||||
public static void removeView(View view) {
|
||||
ViewGroup parent = getParent(view);
|
||||
if (parent != null) {
|
||||
parent.removeView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public static void replaceView(View currentView, View newView) {
|
||||
ViewGroup parent = getParent(currentView);
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
final int index = parent.indexOfChild(currentView);
|
||||
removeView(currentView);
|
||||
parent.addView(newView, index);
|
||||
}
|
||||
|
||||
private int getPdfPasswordEditField() {
|
||||
return R.id.etPassword;
|
||||
}
|
||||
|
||||
private int getPdfPasswordOkButton() {
|
||||
return R.id.btOK;
|
||||
}
|
||||
|
||||
private int getPdfPasswordExitButton() {
|
||||
return R.id.btExit;
|
||||
}
|
||||
|
||||
public interface ActionBarHidden {
|
||||
|
||||
Toolbar getCurrentActivityToolbarReplacedBy(@NonNull final Toolbar toolbar);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 net.sf.andpdf.utils;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.util.FloatMath;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class MathUtils {
|
||||
|
||||
public static float distance(MotionEvent event) {
|
||||
float x = event.getX(0) - event.getX(1);
|
||||
float y = event.getY(0) - event.getY(1);
|
||||
return FloatMath.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
public static float distance(PointF p1, PointF p2) {
|
||||
float x = p1.x - p2.x;
|
||||
float y = p1.y - p2.y;
|
||||
return FloatMath.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
public static float distance(float x1, float y1, float x2, float y2) {
|
||||
float x = x1 - x2;
|
||||
float y = y1 - y2;
|
||||
return FloatMath.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
public static void midpoint(MotionEvent event, PointF point) {
|
||||
float x1 = event.getX(0);
|
||||
float y1 = event.getY(0);
|
||||
float x2 = event.getX(1);
|
||||
float y2 = event.getY(1);
|
||||
midpoint(x1, y1, x2, y2, point);
|
||||
}
|
||||
|
||||
public static void midpoint(float x1, float y1, float x2, float y2, PointF point) {
|
||||
point.x = (x1 + x2) / 2.0f;
|
||||
point.y = (y1 + y2) / 2.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates p1 around p2 by angle degrees.
|
||||
*
|
||||
* @param p1
|
||||
* @param p2
|
||||
* @param angle
|
||||
*/
|
||||
public void rotate(PointF p1, PointF p2, float angle) {
|
||||
float px = p1.x;
|
||||
float py = p1.y;
|
||||
float ox = p2.x;
|
||||
float oy = p2.y;
|
||||
p1.x = (FloatMath.cos(angle) * (px - ox) - FloatMath.sin(angle) * (py - oy) + ox);
|
||||
p1.y = (FloatMath.sin(angle) * (px - ox) + FloatMath.cos(angle) * (py - oy) + oy);
|
||||
}
|
||||
|
||||
public static float angle(PointF p1, PointF p2) {
|
||||
return angle(p1.x, p1.y, p2.x, p2.y);
|
||||
}
|
||||
|
||||
public static float angle(float x1, float y1, float x2, float y2) {
|
||||
return (float) Math.atan2(y2 - y1, x2 - x1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 net.sf.andpdf.utils;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.util.FloatMath;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class VectorF {
|
||||
|
||||
public float angle;
|
||||
public float length;
|
||||
|
||||
public final PointF start = new PointF();
|
||||
public final PointF end = new PointF();
|
||||
|
||||
public void calculateEndPoint() {
|
||||
end.x = FloatMath.cos(angle) * length + start.x;
|
||||
end.y = FloatMath.sin(angle) * length + start.y;
|
||||
}
|
||||
|
||||
public void setStart(PointF p) {
|
||||
this.start.x = p.x;
|
||||
this.start.y = p.y;
|
||||
}
|
||||
|
||||
public void setEnd(PointF p) {
|
||||
this.end.x = p.x;
|
||||
this.end.y = p.y;
|
||||
}
|
||||
|
||||
public void set(MotionEvent event) {
|
||||
this.start.x = event.getX(0);
|
||||
this.start.y = event.getY(0);
|
||||
this.end.x = event.getX(1);
|
||||
this.end.y = event.getY(1);
|
||||
}
|
||||
|
||||
public float calculateLength() {
|
||||
length = MathUtils.distance(start, end);
|
||||
return length;
|
||||
}
|
||||
|
||||
public float calculateAngle() {
|
||||
angle = MathUtils.angle(start, end);
|
||||
return angle;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 282 B |
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<size android:width="3dp"/>
|
||||
<solid android:color="@android:color/black"/>
|
||||
</shape>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#3f192d47"/>
|
||||
</shape>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/global_close_button_shape" android:state_pressed="true"/>
|
||||
</selector>
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="#0c0d0e">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pdf_toolbar_close_image"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginRight="31dp"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/global_close_button_normal"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Preview"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="20sp"/>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pdf_toolbar_page_numbers_text_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="21dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="20sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
|
@ -20,51 +20,32 @@
|
|||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- filename -->
|
||||
<!-- filename -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Password protected PDF-File:"
|
||||
android:textColor="@android:color/black"/>
|
||||
|
||||
<EditText
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:paddingBottom="0dip"
|
||||
android:text="Password protected PDF-File:"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etPassword"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="60dp"
|
||||
android:layout_weight="10"
|
||||
android:hint="please enter password for PDF"
|
||||
android:inputType="textPassword"
|
||||
android:textColor="@android:color/black"
|
||||
android:textColorHint="@android:color/darker_gray"
|
||||
android:textCursorDrawable="@drawable/color_cursor">
|
||||
|
||||
<requestFocus/>
|
||||
android:layout_height="60px"
|
||||
android:text=""
|
||||
android:layout_weight="10"
|
||||
android:hint="please enter password for PDF">
|
||||
<requestFocus />
|
||||
</EditText>
|
||||
|
||||
<!-- Buttons 'OK' + 'Exit' -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btExit"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="40dp"
|
||||
android:text="Exit"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btOK"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="40dp"
|
||||
android:text="OK"/>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<!-- Buttons 'OK' + 'Exit' -->
|
||||
<LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="horizontal" >
|
||||
<Button android:id="@+id/btExit" android:layout_width="120px" android:layout_height="40px" android:text="Exit"></Button>
|
||||
<Button android:id="@+id/btOK" android:layout_width="120px" android:layout_height="40px" android:text="OK"></Button>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="#0c0d0e">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pdf_toolbar_close_image"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginRight="31dp"
|
||||
android:background="@drawable/global_light_selector"
|
||||
android:paddingLeft="7dp"
|
||||
android:paddingRight="7dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/global_close_button_normal"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Preview"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="20sp"/>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pdf_toolbar_page_numbers_text_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="21dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="20sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<drawable name="white">#FFFFFFFF</drawable>
|
||||
<drawable name="black">#000000</drawable>
|
||||
<drawable name="blue">#000000FF</drawable>
|
||||
<drawable name="violet">#9370DB</drawable>
|
||||
|
||||
<color name="zoomed_image_view_background">#1d2022</color>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
apply plugin: 'com.android.library'
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion '23.0.2'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 7
|
||||
targetSdkVersion 7
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.polites.android"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-sdk android:minSdkVersion="7" />
|
||||
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
/**
|
||||
* @author Jason Polites
|
||||
*
|
||||
*/
|
||||
public interface Animation {
|
||||
|
||||
/**
|
||||
* Transforms the view.
|
||||
* @param view
|
||||
* @param diffTime
|
||||
* @return true if this animation should remain active. False otherwise.
|
||||
*/
|
||||
public boolean update(GestureImageView view, long time);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
|
||||
/**
|
||||
* @author Jason Polites
|
||||
*
|
||||
*/
|
||||
public class Animator extends Thread {
|
||||
|
||||
private GestureImageView view;
|
||||
private Animation animation;
|
||||
private boolean running = false;
|
||||
private boolean active = false;
|
||||
private long lastTime = -1L;
|
||||
|
||||
public Animator(GestureImageView view, String threadName) {
|
||||
super(threadName);
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
running = true;
|
||||
|
||||
while(running) {
|
||||
|
||||
while(active && animation != null) {
|
||||
long time = System.currentTimeMillis();
|
||||
active = animation.update(view, time - lastTime);
|
||||
view.redraw();
|
||||
lastTime = time;
|
||||
|
||||
while(active) {
|
||||
try {
|
||||
if(view.waitForDraw(32)) { // 30Htz
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
if(running) {
|
||||
try {
|
||||
wait();
|
||||
}
|
||||
catch (InterruptedException ignore) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void finish() {
|
||||
running = false;
|
||||
active = false;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
public void play(Animation transformer) {
|
||||
if(active) {
|
||||
cancel();
|
||||
}
|
||||
this.animation = transformer;
|
||||
|
||||
activate();
|
||||
}
|
||||
|
||||
public synchronized void activate() {
|
||||
lastTime = System.currentTimeMillis();
|
||||
active = true;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.polites.android;
|
||||
|
||||
/**
|
||||
* @author winney E-mail: weiyixiong@tigerbrokers.com
|
||||
* @version 创建时间: 2015/11/18 下午4:49
|
||||
*/
|
||||
public class Direction {
|
||||
public final static int LEFT = 1;
|
||||
public final static int TOP = 2;
|
||||
public final static int RIGHT = 3;
|
||||
public final static int BOTTOM = 4;
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
/**
|
||||
* @author Jason Polites
|
||||
*
|
||||
*/
|
||||
public class FlingAnimation implements Animation {
|
||||
|
||||
private float velocityX;
|
||||
private float velocityY;
|
||||
|
||||
private float factor = 0.95f;
|
||||
|
||||
private float threshold = 10;
|
||||
|
||||
private FlingAnimationListener listener;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.polites.android.Transformer#update(com.polites.android.GestureImageView, long)
|
||||
*/
|
||||
@Override
|
||||
public boolean update(GestureImageView view, long time) {
|
||||
float seconds = (float) time / 1000.0f;
|
||||
|
||||
float dx = velocityX * seconds;
|
||||
float dy = velocityY * seconds;
|
||||
|
||||
velocityX *= factor;
|
||||
velocityY *= factor;
|
||||
|
||||
boolean active = (Math.abs(velocityX) > threshold && Math.abs(velocityY) > threshold);
|
||||
|
||||
if(listener != null) {
|
||||
listener.onMove(dx, dy);
|
||||
|
||||
if(!active) {
|
||||
listener.onComplete();
|
||||
}
|
||||
}
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setVelocityX(float velocityX) {
|
||||
this.velocityX = velocityX;
|
||||
}
|
||||
|
||||
public void setVelocityY(float velocityY) {
|
||||
this.velocityY = velocityY;
|
||||
}
|
||||
|
||||
public void setFactor(float factor) {
|
||||
this.factor = factor;
|
||||
}
|
||||
|
||||
public void setListener(FlingAnimationListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
|
||||
/**
|
||||
* @author Jason Polites
|
||||
*
|
||||
*/
|
||||
public interface FlingAnimationListener {
|
||||
|
||||
public void onMove(float x, float y);
|
||||
|
||||
public void onComplete();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
import android.view.GestureDetector.SimpleOnGestureListener;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
|
||||
/**
|
||||
* @author Jason Polites
|
||||
*
|
||||
*/
|
||||
public class FlingListener extends SimpleOnGestureListener {
|
||||
|
||||
private float velocityX;
|
||||
private float velocityY;
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
this.velocityX = velocityX;
|
||||
this.velocityY = velocityY;
|
||||
return true;
|
||||
}
|
||||
|
||||
public float getVelocityX() {
|
||||
return velocityX;
|
||||
}
|
||||
|
||||
public float getVelocityY() {
|
||||
return velocityY;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,698 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.ImageView;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class GestureImageView extends ImageView {
|
||||
|
||||
public static final String GLOBAL_NS = "http://schemas.android.com/apk/res/android";
|
||||
public static final String LOCAL_NS = "http://schemas.polites.com/android";
|
||||
|
||||
private final Semaphore drawLock = new Semaphore(0);
|
||||
private Animator animator;
|
||||
|
||||
private Drawable drawable;
|
||||
private int drawableWidth = 0;
|
||||
private int drawableHeight = 0;
|
||||
|
||||
private float x = 0, y = 0;
|
||||
|
||||
private float scaleAdjust = 1.0f;
|
||||
private float startingScale = -1.0f;
|
||||
|
||||
private float scale = 1.0f;
|
||||
private float maxScale = 5.0f;
|
||||
private float minScale = 0.75f;
|
||||
private float fitScaleHorizontal = 1.0f;
|
||||
private float fitScaleVertical = 1.0f;
|
||||
private float rotation = 0.0f;
|
||||
|
||||
private float centerX;
|
||||
private float centerY;
|
||||
|
||||
private Float startX, startY;
|
||||
|
||||
private int hWidth;
|
||||
private int hHeight;
|
||||
|
||||
private int resId = -1;
|
||||
private boolean recycle = false;
|
||||
private boolean strict = false;
|
||||
|
||||
private int displayHeight;
|
||||
private int displayWidth;
|
||||
|
||||
private int alpha = 255;
|
||||
private ColorFilter colorFilter;
|
||||
|
||||
private int deviceOrientation = -1;
|
||||
private int imageOrientation;
|
||||
|
||||
private GestureImageViewListener gestureImageViewListener;
|
||||
private GestureImageViewTouchListener gestureImageViewTouchListener;
|
||||
private GestureImageViewTouchListener.OnReachBoundListener onReachBoundListener;
|
||||
|
||||
private OnTouchListener customOnTouchListener;
|
||||
private OnClickListener onClickListener;
|
||||
|
||||
public GestureImageView(Context context, AttributeSet attrs, int defStyle) {
|
||||
this(context, attrs);
|
||||
}
|
||||
|
||||
public GestureImageView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
String scaleType = attrs.getAttributeValue(GLOBAL_NS, "scaleType");
|
||||
|
||||
if (scaleType == null || scaleType.trim().length() == 0) {
|
||||
setScaleType(ScaleType.CENTER_INSIDE);
|
||||
}
|
||||
|
||||
String strStartX = attrs.getAttributeValue(LOCAL_NS, "start-x");
|
||||
String strStartY = attrs.getAttributeValue(LOCAL_NS, "start-y");
|
||||
|
||||
if (strStartX != null && strStartX.trim().length() > 0) {
|
||||
startX = Float.parseFloat(strStartX);
|
||||
}
|
||||
|
||||
if (strStartY != null && strStartY.trim().length() > 0) {
|
||||
startY = Float.parseFloat(strStartY);
|
||||
}
|
||||
|
||||
setStartingScale(attrs.getAttributeFloatValue(LOCAL_NS, "start-scale", startingScale));
|
||||
setMinScale(attrs.getAttributeFloatValue(LOCAL_NS, "min-scale", minScale));
|
||||
setMaxScale(attrs.getAttributeFloatValue(LOCAL_NS, "max-scale", maxScale));
|
||||
setStrict(attrs.getAttributeBooleanValue(LOCAL_NS, "strict", strict));
|
||||
setRecycle(attrs.getAttributeBooleanValue(LOCAL_NS, "recycle", recycle));
|
||||
|
||||
initImage();
|
||||
}
|
||||
|
||||
public GestureImageView(Context context) {
|
||||
super(context);
|
||||
setScaleType(ScaleType.CENTER_INSIDE);
|
||||
initImage();
|
||||
}
|
||||
|
||||
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
if (drawable != null) {
|
||||
int orientation = getResources().getConfiguration().orientation;
|
||||
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
displayHeight = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
if (getLayoutParams().width == LayoutParams.WRAP_CONTENT) {
|
||||
float ratio = (float) getImageWidth() / (float) getImageHeight();
|
||||
displayWidth = Math.round((float) displayHeight * ratio);
|
||||
} else {
|
||||
displayWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||
}
|
||||
} else {
|
||||
displayWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||
|
||||
if (getLayoutParams().height == LayoutParams.WRAP_CONTENT) {
|
||||
float ratio = (float) getImageHeight() / (float) getImageWidth();
|
||||
displayHeight = Math.round((float) displayWidth * ratio);
|
||||
} else {
|
||||
displayHeight = MeasureSpec.getSize(heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
displayHeight = MeasureSpec.getSize(heightMeasureSpec);
|
||||
displayWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||
}
|
||||
if (displayWidth == 0 && getImageWidth() != 0) {
|
||||
displayWidth = getImageWidth();
|
||||
}
|
||||
if (displayHeight == 0 && getImageHeight() != 0) {
|
||||
displayHeight = getImageHeight();
|
||||
}
|
||||
|
||||
setMeasuredDimension(displayWidth, displayHeight);
|
||||
}
|
||||
|
||||
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
setupCanvas(displayWidth, displayHeight, getResources().getConfiguration().orientation);
|
||||
}
|
||||
|
||||
protected void setupCanvas(int measuredWidth, int measuredHeight, int orientation) {
|
||||
|
||||
if (deviceOrientation != orientation) {
|
||||
deviceOrientation = orientation;
|
||||
}
|
||||
|
||||
if (drawable != null) {
|
||||
int imageWidth = getImageWidth();
|
||||
int imageHeight = getImageHeight();
|
||||
|
||||
hWidth = Math.round(((float) imageWidth / 2.0f));
|
||||
hHeight = Math.round(((float) imageHeight / 2.0f));
|
||||
|
||||
measuredWidth -= (getPaddingLeft() + getPaddingRight());
|
||||
measuredHeight -= (getPaddingTop() + getPaddingBottom());
|
||||
|
||||
computeCropScale(imageWidth, imageHeight, measuredWidth, measuredHeight);
|
||||
|
||||
if (startingScale <= 0.0f) {
|
||||
computeStartingScale(imageWidth, imageHeight, measuredWidth, measuredHeight);
|
||||
}
|
||||
|
||||
scaleAdjust = startingScale;
|
||||
|
||||
this.centerX = (float) measuredWidth / 2.0f;
|
||||
this.centerY = (float) measuredHeight / 2.0f;
|
||||
|
||||
if (startX == null) {
|
||||
x = centerX;
|
||||
} else {
|
||||
x = startX;
|
||||
}
|
||||
|
||||
if (startY == null) {
|
||||
y = centerY;
|
||||
} else {
|
||||
y = startY;
|
||||
}
|
||||
|
||||
gestureImageViewTouchListener = new GestureImageViewTouchListener(this, measuredWidth, measuredHeight);
|
||||
gestureImageViewTouchListener.setOnReachBoundListener(onReachBoundListener);
|
||||
|
||||
if (isLandscape()) {
|
||||
gestureImageViewTouchListener.setMinScale(minScale * fitScaleHorizontal);
|
||||
} else {
|
||||
gestureImageViewTouchListener.setMinScale(minScale * fitScaleVertical);
|
||||
}
|
||||
|
||||
gestureImageViewTouchListener.setMaxScale(maxScale * startingScale);
|
||||
|
||||
gestureImageViewTouchListener.setFitScaleHorizontal(fitScaleHorizontal);
|
||||
gestureImageViewTouchListener.setFitScaleVertical(fitScaleVertical);
|
||||
gestureImageViewTouchListener.setCanvasWidth(measuredWidth);
|
||||
gestureImageViewTouchListener.setCanvasHeight(measuredHeight);
|
||||
gestureImageViewTouchListener.setOnClickListener(onClickListener);
|
||||
|
||||
drawable.setBounds(-hWidth, -hHeight, hWidth, hHeight);
|
||||
|
||||
super.setOnTouchListener(new OnTouchListener() {
|
||||
@Override public boolean onTouch(View v, MotionEvent event) {
|
||||
if (customOnTouchListener != null) {
|
||||
customOnTouchListener.onTouch(v, event);
|
||||
}
|
||||
return gestureImageViewTouchListener.onTouch(v, event);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected void computeCropScale(int imageWidth, int imageHeight, int measuredWidth, int measuredHeight) {
|
||||
fitScaleHorizontal = (float) measuredWidth / (float) imageWidth;
|
||||
fitScaleVertical = (float) measuredHeight / (float) imageHeight;
|
||||
}
|
||||
|
||||
protected void computeStartingScale(int imageWidth, int imageHeight, int measuredWidth, int measuredHeight) {
|
||||
switch (getScaleType()) {
|
||||
case CENTER:
|
||||
// Center the image in the view, but perform no scaling.
|
||||
startingScale = 1.0f;
|
||||
break;
|
||||
|
||||
case CENTER_CROP:
|
||||
// Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions
|
||||
// (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding).
|
||||
startingScale =
|
||||
Math.max((float) measuredHeight / (float) imageHeight, (float) measuredWidth / (float) imageWidth);
|
||||
break;
|
||||
|
||||
case CENTER_INSIDE:
|
||||
|
||||
// Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions
|
||||
// (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding).
|
||||
float wRatio = (float) imageWidth / (float) measuredWidth;
|
||||
float hRatio = (float) imageHeight / (float) measuredHeight;
|
||||
|
||||
if (wRatio > hRatio) {
|
||||
startingScale = fitScaleHorizontal;
|
||||
} else {
|
||||
startingScale = fitScaleVertical;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isRecycled() {
|
||||
if (drawable != null && drawable instanceof BitmapDrawable) {
|
||||
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
|
||||
if (bitmap != null) {
|
||||
return bitmap.isRecycled();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void recycle() {
|
||||
if (recycle && drawable != null && drawable instanceof BitmapDrawable) {
|
||||
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
|
||||
if (bitmap != null) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override protected void onDraw(Canvas canvas) {
|
||||
if (true) {
|
||||
if (drawable != null && !isRecycled()) {
|
||||
if (drawableHeight == 0 && drawableWidth == 0) {
|
||||
return;
|
||||
}
|
||||
canvas.save();
|
||||
|
||||
float adjustedScale = scale * scaleAdjust;
|
||||
|
||||
canvas.translate(x, y);
|
||||
|
||||
if (rotation != 0.0f) {
|
||||
canvas.rotate(rotation);
|
||||
}
|
||||
|
||||
if (adjustedScale != 1.0f) {
|
||||
canvas.scale(adjustedScale, adjustedScale);
|
||||
}
|
||||
//drawable.setBounds(0, 0, drawableWidth, drawableHeight);
|
||||
drawable.draw(canvas);
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
if (drawLock.availablePermits() <= 0) {
|
||||
drawLock.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a draw
|
||||
*
|
||||
* @param max time to wait for draw (ms)
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public boolean waitForDraw(long timeout) throws InterruptedException {
|
||||
return drawLock.tryAcquire(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override protected void onAttachedToWindow() {
|
||||
animator = new Animator(this, "GestureImageViewAnimator");
|
||||
animator.start();
|
||||
|
||||
if (resId >= 0 && drawable == null) {
|
||||
setImageResource(resId);
|
||||
}
|
||||
|
||||
super.onAttachedToWindow();
|
||||
}
|
||||
|
||||
public void animationStart(Animation animation) {
|
||||
if (animator != null) {
|
||||
animator.play(animation);
|
||||
}
|
||||
}
|
||||
|
||||
public void animationStop() {
|
||||
if (animator != null) {
|
||||
animator.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override protected void onDetachedFromWindow() {
|
||||
if (animator != null) {
|
||||
animator.finish();
|
||||
}
|
||||
if (recycle && drawable != null && !isRecycled()) {
|
||||
recycle();
|
||||
drawable = null;
|
||||
}
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
protected void initImage() {
|
||||
if (this.drawable != null) {
|
||||
drawableWidth = drawable.getIntrinsicWidth();
|
||||
drawableHeight = drawable.getIntrinsicHeight();
|
||||
this.drawable.setAlpha(alpha);
|
||||
this.drawable.setFilterBitmap(true);
|
||||
if (colorFilter != null) {
|
||||
this.drawable.setColorFilter(colorFilter);
|
||||
}
|
||||
}
|
||||
|
||||
requestLayout();
|
||||
redraw();
|
||||
}
|
||||
|
||||
public void setImageBitmap(Bitmap image) {
|
||||
this.drawable = new BitmapDrawable(getResources(), image);
|
||||
initImage();
|
||||
}
|
||||
|
||||
@Override public void setImageDrawable(Drawable drawable) {
|
||||
this.drawable = drawable;
|
||||
initImage();
|
||||
}
|
||||
|
||||
public void setImageResource(int id) {
|
||||
if (this.drawable != null) {
|
||||
this.recycle();
|
||||
}
|
||||
if (id >= 0) {
|
||||
this.resId = id;
|
||||
setImageDrawable(getContext().getResources().getDrawable(id));
|
||||
}
|
||||
}
|
||||
|
||||
public int getScaledWidth() {
|
||||
return Math.round(getImageWidth() * getScale());
|
||||
}
|
||||
|
||||
public int getScaledHeight() {
|
||||
return Math.round(getImageHeight() * getScale());
|
||||
}
|
||||
|
||||
public int getImageWidth() {
|
||||
if (drawable != null) {
|
||||
return drawable.getIntrinsicWidth();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getImageHeight() {
|
||||
if (drawable != null) {
|
||||
return drawable.getIntrinsicHeight();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void moveBy(float x, float y) {
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
}
|
||||
|
||||
public void setPosition(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public void redraw() {
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
public void setMinScale(float min) {
|
||||
this.minScale = min;
|
||||
if (gestureImageViewTouchListener != null) {
|
||||
gestureImageViewTouchListener.setMinScale(min * fitScaleHorizontal);
|
||||
}
|
||||
}
|
||||
|
||||
public void setMaxScale(float max) {
|
||||
this.maxScale = max;
|
||||
if (gestureImageViewTouchListener != null) {
|
||||
gestureImageViewTouchListener.setMaxScale(max * startingScale);
|
||||
}
|
||||
}
|
||||
|
||||
public void setScale(float scale) {
|
||||
scaleAdjust = scale;
|
||||
}
|
||||
|
||||
public float getScale() {
|
||||
return scaleAdjust;
|
||||
}
|
||||
|
||||
public float getImageX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public float getImageY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public boolean isStrict() {
|
||||
return strict;
|
||||
}
|
||||
|
||||
public void setStrict(boolean strict) {
|
||||
this.strict = strict;
|
||||
}
|
||||
|
||||
public boolean isRecycle() {
|
||||
return recycle;
|
||||
}
|
||||
|
||||
public void setRecycle(boolean recycle) {
|
||||
this.recycle = recycle;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
x = centerX;
|
||||
y = centerY;
|
||||
scaleAdjust = startingScale;
|
||||
if (gestureImageViewTouchListener != null) {
|
||||
gestureImageViewTouchListener.reset();
|
||||
}
|
||||
redraw();
|
||||
}
|
||||
|
||||
public void setRotation(float rotation) {
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
public void setGestureImageViewListener(GestureImageViewListener pinchImageViewListener) {
|
||||
this.gestureImageViewListener = pinchImageViewListener;
|
||||
}
|
||||
|
||||
public GestureImageViewListener getGestureImageViewListener() {
|
||||
return gestureImageViewListener;
|
||||
}
|
||||
|
||||
@Override public Drawable getDrawable() {
|
||||
return drawable;
|
||||
}
|
||||
|
||||
@Override public void setAlpha(int alpha) {
|
||||
this.alpha = alpha;
|
||||
if (drawable != null) {
|
||||
drawable.setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void setColorFilter(ColorFilter cf) {
|
||||
this.colorFilter = cf;
|
||||
if (drawable != null) {
|
||||
drawable.setColorFilter(cf);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void setImageURI(Uri mUri) {
|
||||
if ("content".equals(mUri.getScheme())) {
|
||||
try {
|
||||
String[] orientationColumn = { MediaStore.Images.Media.ORIENTATION };
|
||||
|
||||
Cursor cur = getContext().getContentResolver().query(mUri, orientationColumn, null, null, null);
|
||||
|
||||
if (cur != null && cur.moveToFirst()) {
|
||||
imageOrientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
|
||||
}
|
||||
|
||||
InputStream in = null;
|
||||
|
||||
try {
|
||||
in = getContext().getContentResolver().openInputStream(mUri);
|
||||
Bitmap bmp = BitmapFactory.decodeStream(in);
|
||||
|
||||
if (imageOrientation != 0) {
|
||||
Matrix m = new Matrix();
|
||||
m.postRotate(imageOrientation);
|
||||
Bitmap rotated = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
|
||||
bmp.recycle();
|
||||
setImageDrawable(new BitmapDrawable(getResources(), rotated));
|
||||
} else {
|
||||
setImageDrawable(new BitmapDrawable(getResources(), bmp));
|
||||
}
|
||||
} finally {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
|
||||
if (cur != null) {
|
||||
cur.close();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w("GestureImageView", "Unable to open content: " + mUri, e);
|
||||
}
|
||||
} else {
|
||||
setImageDrawable(Drawable.createFromPath(mUri.toString()));
|
||||
}
|
||||
|
||||
if (drawable == null) {
|
||||
Log.e("GestureImageView", "resolveUri failed on bad bitmap uri: " + mUri);
|
||||
// Don't try again.
|
||||
mUri = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public Matrix getImageMatrix() {
|
||||
if (strict) {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
return super.getImageMatrix();
|
||||
}
|
||||
|
||||
@Override public void setScaleType(ScaleType scaleType) {
|
||||
if (scaleType == ScaleType.CENTER ||
|
||||
scaleType == ScaleType.CENTER_CROP ||
|
||||
scaleType == ScaleType.CENTER_INSIDE) {
|
||||
|
||||
super.setScaleType(scaleType);
|
||||
} else if (strict) {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void invalidateDrawable(Drawable dr) {
|
||||
if (strict) {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
super.invalidateDrawable(dr);
|
||||
}
|
||||
|
||||
@Override public int[] onCreateDrawableState(int extraSpace) {
|
||||
if (strict) {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
return super.onCreateDrawableState(extraSpace);
|
||||
}
|
||||
|
||||
@Override public void setAdjustViewBounds(boolean adjustViewBounds) {
|
||||
if (strict) {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
super.setAdjustViewBounds(adjustViewBounds);
|
||||
}
|
||||
|
||||
@Override public void setImageLevel(int level) {
|
||||
if (strict) {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
super.setImageLevel(level);
|
||||
}
|
||||
|
||||
@Override public void setImageMatrix(Matrix matrix) {
|
||||
if (strict) {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void setImageState(int[] state, boolean merge) {
|
||||
if (strict) {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void setSelected(boolean selected) {
|
||||
if (strict) {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
super.setSelected(selected);
|
||||
}
|
||||
|
||||
@Override public void setOnTouchListener(OnTouchListener l) {
|
||||
this.customOnTouchListener = l;
|
||||
}
|
||||
|
||||
public void setOnReachBoundListener(GestureImageViewTouchListener.OnReachBoundListener onReachBoundListener) {
|
||||
this.onReachBoundListener = onReachBoundListener;
|
||||
}
|
||||
|
||||
public float getCenterX() {
|
||||
return centerX;
|
||||
}
|
||||
|
||||
public float getCenterY() {
|
||||
return centerY;
|
||||
}
|
||||
|
||||
public boolean isLandscape() {
|
||||
return getImageWidth() >= getImageHeight();
|
||||
}
|
||||
|
||||
public boolean isPortrait() {
|
||||
return getImageWidth() <= getImageHeight();
|
||||
}
|
||||
|
||||
public void setStartingScale(float startingScale) {
|
||||
this.startingScale = startingScale;
|
||||
}
|
||||
|
||||
public void setStartingPosition(float x, float y) {
|
||||
this.startX = x;
|
||||
this.startY = y;
|
||||
}
|
||||
|
||||
@Override public void setOnClickListener(OnClickListener l) {
|
||||
this.onClickListener = l;
|
||||
|
||||
if (gestureImageViewTouchListener != null) {
|
||||
gestureImageViewTouchListener.setOnClickListener(l);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the image dimensions are aligned with the orientation of the device.
|
||||
*/
|
||||
public boolean isOrientationAligned() {
|
||||
if (deviceOrientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
return isLandscape();
|
||||
} else if (deviceOrientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
return isPortrait();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getDeviceOrientation() {
|
||||
return deviceOrientation;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
/**
|
||||
* @author jasonpolites
|
||||
*
|
||||
*/
|
||||
public interface GestureImageViewListener {
|
||||
|
||||
public void onTouch(float x, float y);
|
||||
|
||||
public void onScale(float scale);
|
||||
|
||||
public void onPosition(float x, float y);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,549 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.PointF;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.GestureDetector.SimpleOnGestureListener;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnTouchListener;
|
||||
|
||||
public class GestureImageViewTouchListener implements OnTouchListener {
|
||||
|
||||
private GestureImageView image;
|
||||
private OnClickListener onClickListener;
|
||||
private OnReachBoundListener onReachBoundListener;
|
||||
private final PointF current = new PointF();
|
||||
private final PointF last = new PointF();
|
||||
private final PointF next = new PointF();
|
||||
private final PointF down = new PointF();
|
||||
private final PointF midpoint = new PointF();
|
||||
|
||||
private final VectorF scaleVector = new VectorF();
|
||||
private final VectorF pinchVector = new VectorF();
|
||||
|
||||
private boolean touched = false;
|
||||
private boolean inZoom = false;
|
||||
|
||||
private float initialDistance;
|
||||
private float lastScale = 1.0f;
|
||||
private float currentScale = 1.0f;
|
||||
|
||||
private float boundaryLeft = 0;
|
||||
private float boundaryTop = 0;
|
||||
private float boundaryRight = 0;
|
||||
private float boundaryBottom = 0;
|
||||
|
||||
private float maxScale = 5.0f;
|
||||
private float minScale = 0.25f;
|
||||
private float fitScaleHorizontal = 1.0f;
|
||||
private float fitScaleVertical = 1.0f;
|
||||
|
||||
private int canvasWidth = 0;
|
||||
private int canvasHeight = 0;
|
||||
|
||||
private float centerX = 0;
|
||||
private float centerY = 0;
|
||||
|
||||
private float startingScale = 0;
|
||||
|
||||
private boolean canDragX = false;
|
||||
private boolean canDragY = false;
|
||||
|
||||
private boolean multiTouch = false;
|
||||
|
||||
private int displayWidth;
|
||||
private int displayHeight;
|
||||
|
||||
private int imageWidth;
|
||||
private int imageHeight;
|
||||
|
||||
private FlingListener flingListener;
|
||||
private FlingAnimation flingAnimation;
|
||||
private ZoomAnimation zoomAnimation;
|
||||
private MoveAnimation moveAnimation;
|
||||
private GestureDetector tapDetector;
|
||||
private GestureDetector flingDetector;
|
||||
private GestureImageViewListener imageListener;
|
||||
|
||||
private int dragSlop = 48;
|
||||
|
||||
public interface OnReachBoundListener {
|
||||
void onReach(View view, int direction);
|
||||
}
|
||||
|
||||
public GestureImageViewTouchListener(final GestureImageView image, int displayWidth, int displayHeight) {
|
||||
super();
|
||||
|
||||
this.image = image;
|
||||
|
||||
this.displayWidth = displayWidth;
|
||||
this.displayHeight = displayHeight;
|
||||
|
||||
this.centerX = (float) displayWidth / 2.0f;
|
||||
this.centerY = (float) displayHeight / 2.0f;
|
||||
|
||||
this.imageWidth = image.getImageWidth();
|
||||
this.imageHeight = image.getImageHeight();
|
||||
|
||||
startingScale = image.getScale();
|
||||
|
||||
currentScale = startingScale;
|
||||
lastScale = startingScale;
|
||||
|
||||
boundaryRight = displayWidth;
|
||||
boundaryBottom = displayHeight;
|
||||
boundaryLeft = 0;
|
||||
boundaryTop = 0;
|
||||
|
||||
next.x = image.getImageX();
|
||||
next.y = image.getImageY();
|
||||
|
||||
flingListener = new FlingListener();
|
||||
flingAnimation = new FlingAnimation();
|
||||
zoomAnimation = new ZoomAnimation();
|
||||
moveAnimation = new MoveAnimation();
|
||||
|
||||
flingAnimation.setListener(new FlingAnimationListener() {
|
||||
@Override public void onMove(float x, float y) {
|
||||
handleDrag(current.x + x, current.y + y);
|
||||
}
|
||||
|
||||
@Override public void onComplete() {
|
||||
}
|
||||
});
|
||||
|
||||
zoomAnimation.setZoom(2.0f);
|
||||
zoomAnimation.setZoomAnimationListener(new ZoomAnimationListener() {
|
||||
@Override public void onZoom(float scale, float x, float y) {
|
||||
if (scale <= maxScale && scale >= minScale) {
|
||||
handleScale(scale, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onComplete() {
|
||||
inZoom = false;
|
||||
handleUp();
|
||||
}
|
||||
});
|
||||
|
||||
moveAnimation.setMoveAnimationListener(new MoveAnimationListener() {
|
||||
|
||||
@Override public void onMove(float x, float y) {
|
||||
image.setPosition(x, y);
|
||||
image.redraw();
|
||||
}
|
||||
});
|
||||
|
||||
tapDetector = new GestureDetector(image.getContext(), new SimpleOnGestureListener() {
|
||||
@Override public boolean onDoubleTap(MotionEvent e) {
|
||||
startZoom(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
if (!inZoom) {
|
||||
if (onClickListener != null) {
|
||||
onClickListener.onClick(image);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
flingDetector = new GestureDetector(image.getContext(), flingListener);
|
||||
imageListener = image.getGestureImageViewListener();
|
||||
|
||||
calculateBoundaries();
|
||||
}
|
||||
|
||||
private void startFling() {
|
||||
flingAnimation.setVelocityX(flingListener.getVelocityX());
|
||||
flingAnimation.setVelocityY(flingListener.getVelocityY());
|
||||
image.animationStart(flingAnimation);
|
||||
}
|
||||
|
||||
private void startZoom(MotionEvent e) {
|
||||
inZoom = true;
|
||||
zoomAnimation.reset();
|
||||
|
||||
float zoomTo;
|
||||
|
||||
if (image.isLandscape()) {
|
||||
if (image.getDeviceOrientation() == Configuration.ORIENTATION_PORTRAIT) {
|
||||
int scaledHeight = image.getScaledHeight();
|
||||
|
||||
if (scaledHeight < canvasHeight) {
|
||||
zoomTo = fitScaleVertical / currentScale;
|
||||
zoomAnimation.setTouchX(e.getX());
|
||||
zoomAnimation.setTouchY(image.getCenterY());
|
||||
} else {
|
||||
zoomTo = fitScaleHorizontal / currentScale;
|
||||
zoomAnimation.setTouchX(image.getCenterX());
|
||||
zoomAnimation.setTouchY(image.getCenterY());
|
||||
}
|
||||
} else {
|
||||
int scaledWidth = image.getScaledWidth();
|
||||
|
||||
if (scaledWidth == canvasWidth) {
|
||||
zoomTo = currentScale * 4.0f;
|
||||
zoomAnimation.setTouchX(e.getX());
|
||||
zoomAnimation.setTouchY(e.getY());
|
||||
} else if (scaledWidth < canvasWidth) {
|
||||
zoomTo = fitScaleHorizontal / currentScale;
|
||||
zoomAnimation.setTouchX(image.getCenterX());
|
||||
zoomAnimation.setTouchY(e.getY());
|
||||
} else {
|
||||
zoomTo = fitScaleHorizontal / currentScale;
|
||||
zoomAnimation.setTouchX(image.getCenterX());
|
||||
zoomAnimation.setTouchY(image.getCenterY());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (image.getDeviceOrientation() == Configuration.ORIENTATION_PORTRAIT) {
|
||||
|
||||
int scaledHeight = image.getScaledHeight();
|
||||
|
||||
if (scaledHeight == canvasHeight) {
|
||||
zoomTo = currentScale * 4.0f;
|
||||
zoomAnimation.setTouchX(e.getX());
|
||||
zoomAnimation.setTouchY(e.getY());
|
||||
} else if (scaledHeight < canvasHeight) {
|
||||
zoomTo = fitScaleVertical / currentScale;
|
||||
zoomAnimation.setTouchX(e.getX());
|
||||
zoomAnimation.setTouchY(image.getCenterY());
|
||||
} else {
|
||||
zoomTo = fitScaleVertical / currentScale;
|
||||
zoomAnimation.setTouchX(image.getCenterX());
|
||||
zoomAnimation.setTouchY(image.getCenterY());
|
||||
}
|
||||
} else {
|
||||
int scaledWidth = image.getScaledWidth();
|
||||
|
||||
if (scaledWidth < canvasWidth) {
|
||||
zoomTo = fitScaleHorizontal / currentScale;
|
||||
zoomAnimation.setTouchX(image.getCenterX());
|
||||
zoomAnimation.setTouchY(e.getY());
|
||||
} else {
|
||||
zoomTo = fitScaleVertical / currentScale;
|
||||
zoomAnimation.setTouchX(image.getCenterX());
|
||||
zoomAnimation.setTouchY(image.getCenterY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zoomAnimation.setZoom(zoomTo);
|
||||
image.animationStart(zoomAnimation);
|
||||
}
|
||||
|
||||
private void stopAnimations() {
|
||||
image.animationStop();
|
||||
}
|
||||
|
||||
@Override public boolean onTouch(View v, MotionEvent event) {
|
||||
|
||||
if (inZoom || inZoom && tapDetector.onTouchEvent(event)) {
|
||||
//if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
// last.x = event.getX();
|
||||
// last.y = event.getY();
|
||||
//} else if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
// if (event.getX() - last.x > dragSlop) {
|
||||
// callReachBound(Direction.LEFT);
|
||||
// } else if (last.x - event.getX() > dragSlop) {
|
||||
// callReachBound(Direction.RIGHT);
|
||||
// }
|
||||
//}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.getPointerCount() == 1 && flingDetector.onTouchEvent(event)) {
|
||||
startFling();
|
||||
}
|
||||
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
handleUp();
|
||||
} else if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
stopAnimations();
|
||||
down.x = event.getX();
|
||||
down.y = event.getY();
|
||||
last.x = event.getX();
|
||||
last.y = event.getY();
|
||||
|
||||
if (imageListener != null) {
|
||||
imageListener.onTouch(last.x, last.y);
|
||||
}
|
||||
|
||||
touched = true;
|
||||
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
|
||||
if (event.getPointerCount() > 1) {
|
||||
multiTouch = true;
|
||||
if (initialDistance > 0) {
|
||||
|
||||
pinchVector.set(event);
|
||||
pinchVector.calculateLength();
|
||||
|
||||
float distance = pinchVector.length;
|
||||
|
||||
if (initialDistance != distance) {
|
||||
|
||||
float newScale = (distance / initialDistance) * lastScale;
|
||||
|
||||
if (newScale <= maxScale) {
|
||||
scaleVector.length *= newScale;
|
||||
|
||||
scaleVector.calculateEndPoint();
|
||||
|
||||
scaleVector.length /= newScale;
|
||||
|
||||
float newX = scaleVector.end.x;
|
||||
float newY = scaleVector.end.y;
|
||||
|
||||
handleScale(newScale, newX, newY);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
initialDistance = MathUtils.distance(event);
|
||||
|
||||
MathUtils.midpoint(event, midpoint);
|
||||
|
||||
scaleVector.setStart(midpoint);
|
||||
scaleVector.setEnd(next);
|
||||
|
||||
scaleVector.calculateLength();
|
||||
scaleVector.calculateAngle();
|
||||
|
||||
scaleVector.length /= lastScale;
|
||||
}
|
||||
} else {
|
||||
if (!touched) {
|
||||
touched = true;
|
||||
last.x = event.getX();
|
||||
last.y = event.getY();
|
||||
next.x = image.getImageX();
|
||||
next.y = image.getImageY();
|
||||
} else if (!multiTouch) {
|
||||
if (handleDrag(event.getX(), event.getY())) {
|
||||
image.redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void handleUp() {
|
||||
|
||||
multiTouch = false;
|
||||
|
||||
initialDistance = 0;
|
||||
lastScale = currentScale;
|
||||
|
||||
//TODO can not work when Zoom state
|
||||
if (!canDragX && current.x - down.x > dragSlop) {
|
||||
callReachBound(Direction.LEFT);
|
||||
} else if (!canDragX && down.x - current.x > dragSlop) {
|
||||
callReachBound(Direction.RIGHT);
|
||||
} else if (boundaryTop == 0 && current.x - down.y > dragSlop * 0.7) {
|
||||
callReachBound(Direction.TOP);
|
||||
} else if (boundaryBottom == 0 && down.y - current.x > dragSlop * 0.7) {
|
||||
callReachBound(Direction.BOTTOM);
|
||||
}
|
||||
if (!canDragX) {
|
||||
next.x = centerX;
|
||||
}
|
||||
|
||||
if (!canDragY) {
|
||||
next.y = centerY;
|
||||
}
|
||||
|
||||
boundCoordinates();
|
||||
|
||||
if (!canDragX && !canDragY) {
|
||||
|
||||
if (image.isLandscape()) {
|
||||
currentScale = fitScaleHorizontal;
|
||||
lastScale = fitScaleHorizontal;
|
||||
} else {
|
||||
currentScale = fitScaleVertical;
|
||||
lastScale = fitScaleVertical;
|
||||
}
|
||||
}
|
||||
|
||||
image.setScale(currentScale);
|
||||
image.setPosition(next.x, next.y);
|
||||
|
||||
if (imageListener != null) {
|
||||
imageListener.onScale(currentScale);
|
||||
imageListener.onPosition(next.x, next.y);
|
||||
}
|
||||
|
||||
image.redraw();
|
||||
}
|
||||
|
||||
protected void handleScale(float scale, float x, float y) {
|
||||
|
||||
currentScale = scale;
|
||||
|
||||
if (currentScale > maxScale) {
|
||||
currentScale = maxScale;
|
||||
} else if (currentScale < minScale) {
|
||||
currentScale = minScale;
|
||||
} else {
|
||||
next.x = x;
|
||||
next.y = y;
|
||||
}
|
||||
|
||||
calculateBoundaries();
|
||||
|
||||
image.setScale(currentScale);
|
||||
image.setPosition(next.x, next.y);
|
||||
|
||||
if (imageListener != null) {
|
||||
imageListener.onScale(currentScale);
|
||||
imageListener.onPosition(next.x, next.y);
|
||||
}
|
||||
|
||||
image.redraw();
|
||||
}
|
||||
|
||||
protected boolean handleDrag(float x, float y) {
|
||||
current.x = x;
|
||||
current.y = y;
|
||||
|
||||
float diffX = (current.x - last.x);
|
||||
float diffY = (current.y - last.y);
|
||||
|
||||
if (diffX != 0 || diffY != 0) {
|
||||
|
||||
if (canDragX) next.x += diffX;
|
||||
if (canDragY) next.y += diffY;
|
||||
|
||||
boundCoordinates();
|
||||
|
||||
last.x = current.x;
|
||||
last.y = current.y;
|
||||
|
||||
if (canDragX || canDragY) {
|
||||
image.setPosition(next.x, next.y);
|
||||
|
||||
if (imageListener != null) {
|
||||
imageListener.onPosition(next.x, next.y);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
currentScale = startingScale;
|
||||
next.x = centerX;
|
||||
next.y = centerY;
|
||||
calculateBoundaries();
|
||||
image.setScale(currentScale);
|
||||
image.setPosition(next.x, next.y);
|
||||
image.redraw();
|
||||
}
|
||||
|
||||
public float getMaxScale() {
|
||||
return maxScale;
|
||||
}
|
||||
|
||||
public void setMaxScale(float maxScale) {
|
||||
this.maxScale = maxScale;
|
||||
}
|
||||
|
||||
public float getMinScale() {
|
||||
return minScale;
|
||||
}
|
||||
|
||||
public void setMinScale(float minScale) {
|
||||
this.minScale = minScale;
|
||||
}
|
||||
|
||||
public void setOnClickListener(OnClickListener onClickListener) {
|
||||
this.onClickListener = onClickListener;
|
||||
}
|
||||
|
||||
public void setOnReachBoundListener(OnReachBoundListener onReachBoundListener) {
|
||||
this.onReachBoundListener = onReachBoundListener;
|
||||
}
|
||||
|
||||
protected void setCanvasWidth(int canvasWidth) {
|
||||
this.canvasWidth = canvasWidth;
|
||||
}
|
||||
|
||||
protected void setCanvasHeight(int canvasHeight) {
|
||||
this.canvasHeight = canvasHeight;
|
||||
}
|
||||
|
||||
protected void setFitScaleHorizontal(float fitScale) {
|
||||
this.fitScaleHorizontal = fitScale;
|
||||
}
|
||||
|
||||
protected void setFitScaleVertical(float fitScaleVertical) {
|
||||
this.fitScaleVertical = fitScaleVertical;
|
||||
}
|
||||
|
||||
protected void boundCoordinates() {
|
||||
|
||||
if (next.x < boundaryLeft) {
|
||||
next.x = boundaryLeft;
|
||||
} else if (next.x > boundaryRight) {
|
||||
next.x = boundaryRight;
|
||||
}
|
||||
if (next.y < boundaryTop) {
|
||||
next.y = boundaryTop;
|
||||
} else if (next.y > boundaryBottom) {
|
||||
next.y = boundaryBottom;
|
||||
}
|
||||
}
|
||||
|
||||
protected void callReachBound(int direction) {
|
||||
if (onReachBoundListener != null) {
|
||||
onReachBoundListener.onReach(image, direction);
|
||||
}
|
||||
}
|
||||
|
||||
protected void calculateBoundaries() {
|
||||
|
||||
int effectiveWidth = Math.round((float) imageWidth * currentScale);
|
||||
int effectiveHeight = Math.round((float) imageHeight * currentScale);
|
||||
|
||||
canDragX = effectiveWidth > displayWidth;
|
||||
canDragY = effectiveHeight > displayHeight;
|
||||
|
||||
if (canDragX) {
|
||||
float diff = (float) (effectiveWidth - displayWidth) / 2.0f;
|
||||
boundaryLeft = centerX - diff;
|
||||
boundaryRight = centerX + diff;
|
||||
}
|
||||
|
||||
if (canDragY) {
|
||||
float diff = (float) (effectiveHeight - displayHeight) / 2.0f;
|
||||
boundaryTop = centerY - diff;
|
||||
boundaryBottom = centerY + diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class MathUtils {
|
||||
|
||||
public static float distance(MotionEvent event) {
|
||||
float x = event.getX(0) - event.getX(1);
|
||||
float y = event.getY(0) - event.getY(1);
|
||||
return (float) Math.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
public static float distance(PointF p1, PointF p2) {
|
||||
float x = p1.x - p2.x;
|
||||
float y = p1.y - p2.y;
|
||||
return (float) Math.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
public static float distance(float x1, float y1, float x2, float y2) {
|
||||
float x = x1 - x2;
|
||||
float y = y1 - y2;
|
||||
return (float) Math.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
public static void midpoint(MotionEvent event, PointF point) {
|
||||
float x1 = event.getX(0);
|
||||
float y1 = event.getY(0);
|
||||
float x2 = event.getX(1);
|
||||
float y2 = event.getY(1);
|
||||
midpoint(x1, y1, x2, y2, point);
|
||||
}
|
||||
|
||||
public static void midpoint(float x1, float y1, float x2, float y2, PointF point) {
|
||||
point.x = (x1 + x2) / 2.0f;
|
||||
point.y = (y1 + y2) / 2.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates p1 around p2 by angle degrees.
|
||||
*/
|
||||
public void rotate(PointF p1, PointF p2, float angle) {
|
||||
float px = p1.x;
|
||||
float py = p1.y;
|
||||
float ox = p2.x;
|
||||
float oy = p2.y;
|
||||
p1.x = (float) (Math.cos(angle) * (px - ox) - Math.sin(angle) * (py - oy) + ox);
|
||||
p1.y = (float) (Math.sin(angle) * (px - ox) + Math.cos(angle) * (py - oy) + oy);
|
||||
}
|
||||
|
||||
public static float angle(PointF p1, PointF p2) {
|
||||
return angle(p1.x, p1.y, p2.x, p2.y);
|
||||
}
|
||||
|
||||
public static float angle(float x1, float y1, float x2, float y2) {
|
||||
return (float) Math.atan2(y2 - y1, x2 - x1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
|
||||
/**
|
||||
* @author Jason Polites
|
||||
*
|
||||
*/
|
||||
public class MoveAnimation implements Animation {
|
||||
|
||||
private boolean firstFrame = true;
|
||||
|
||||
private float startX;
|
||||
private float startY;
|
||||
|
||||
private float targetX;
|
||||
private float targetY;
|
||||
private long animationTimeMS = 100;
|
||||
private long totalTime = 0;
|
||||
|
||||
private MoveAnimationListener moveAnimationListener;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.polites.android.Animation#update(com.polites.android.GestureImageView, long)
|
||||
*/
|
||||
@Override
|
||||
public boolean update(GestureImageView view, long time) {
|
||||
totalTime += time;
|
||||
|
||||
if(firstFrame) {
|
||||
firstFrame = false;
|
||||
startX = view.getImageX();
|
||||
startY = view.getImageY();
|
||||
}
|
||||
|
||||
if(totalTime < animationTimeMS) {
|
||||
|
||||
float ratio = (float) totalTime / animationTimeMS;
|
||||
|
||||
float newX = ((targetX - startX) * ratio) + startX;
|
||||
float newY = ((targetY - startY) * ratio) + startY;
|
||||
|
||||
if(moveAnimationListener != null) {
|
||||
moveAnimationListener.onMove(newX, newY);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if(moveAnimationListener != null) {
|
||||
moveAnimationListener.onMove(targetX, targetY);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
firstFrame = true;
|
||||
totalTime = 0;
|
||||
}
|
||||
|
||||
|
||||
public float getTargetX() {
|
||||
return targetX;
|
||||
}
|
||||
|
||||
|
||||
public void setTargetX(float targetX) {
|
||||
this.targetX = targetX;
|
||||
}
|
||||
|
||||
|
||||
public float getTargetY() {
|
||||
return targetY;
|
||||
}
|
||||
|
||||
public void setTargetY(float targetY) {
|
||||
this.targetY = targetY;
|
||||
}
|
||||
|
||||
public long getAnimationTimeMS() {
|
||||
return animationTimeMS;
|
||||
}
|
||||
|
||||
public void setAnimationTimeMS(long animationTimeMS) {
|
||||
this.animationTimeMS = animationTimeMS;
|
||||
}
|
||||
|
||||
public void setMoveAnimationListener(MoveAnimationListener moveAnimationListener) {
|
||||
this.moveAnimationListener = moveAnimationListener;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
|
||||
/**
|
||||
* @author Jason Polites
|
||||
*
|
||||
*/
|
||||
public interface MoveAnimationListener {
|
||||
|
||||
public void onMove(float x, float y);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class VectorF {
|
||||
|
||||
public float angle;
|
||||
public float length;
|
||||
|
||||
public final PointF start = new PointF();
|
||||
public final PointF end = new PointF();
|
||||
|
||||
public void calculateEndPoint() {
|
||||
end.x = (float) (Math.cos(angle) * length + start.x);
|
||||
end.y = (float) (Math.sin(angle) * length + start.y);
|
||||
}
|
||||
|
||||
public void setStart(PointF p) {
|
||||
this.start.x = p.x;
|
||||
this.start.y = p.y;
|
||||
}
|
||||
|
||||
public void setEnd(PointF p) {
|
||||
this.end.x = p.x;
|
||||
this.end.y = p.y;
|
||||
}
|
||||
|
||||
public void set(MotionEvent event) {
|
||||
this.start.x = event.getX(0);
|
||||
this.start.y = event.getY(0);
|
||||
this.end.x = event.getX(1);
|
||||
this.end.y = event.getY(1);
|
||||
}
|
||||
|
||||
public float calculateLength() {
|
||||
length = MathUtils.distance(start, end);
|
||||
return length;
|
||||
}
|
||||
|
||||
public float calculateAngle() {
|
||||
angle = MathUtils.angle(start, end);
|
||||
return angle;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
import android.graphics.PointF;
|
||||
|
||||
|
||||
/**
|
||||
* @author Jason Polites
|
||||
*
|
||||
*/
|
||||
public class ZoomAnimation implements Animation {
|
||||
|
||||
private boolean firstFrame = true;
|
||||
|
||||
private float touchX;
|
||||
private float touchY;
|
||||
|
||||
private float zoom;
|
||||
|
||||
private float startX;
|
||||
private float startY;
|
||||
private float startScale;
|
||||
|
||||
private float xDiff;
|
||||
private float yDiff;
|
||||
private float scaleDiff;
|
||||
|
||||
private long animationLengthMS = 200;
|
||||
private long totalTime = 0;
|
||||
|
||||
private ZoomAnimationListener zoomAnimationListener;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.polites.android.Animation#update(com.polites.android.GestureImageView, long)
|
||||
*/
|
||||
@Override
|
||||
public boolean update(GestureImageView view, long time) {
|
||||
if(firstFrame) {
|
||||
firstFrame = false;
|
||||
|
||||
startX = view.getImageX();
|
||||
startY = view.getImageY();
|
||||
startScale = view.getScale();
|
||||
scaleDiff = (zoom * startScale) - startScale;
|
||||
|
||||
if(scaleDiff > 0) {
|
||||
// Calculate destination for midpoint
|
||||
VectorF vector = new VectorF();
|
||||
|
||||
// Set the touch point as start because we want to move the end
|
||||
vector.setStart(new PointF(touchX, touchY));
|
||||
vector.setEnd(new PointF(startX, startY));
|
||||
|
||||
vector.calculateAngle();
|
||||
|
||||
// Get the current length
|
||||
float length = vector.calculateLength();
|
||||
|
||||
// Multiply length by zoom to get the new length
|
||||
vector.length = length*zoom;
|
||||
|
||||
// Now deduce the new endpoint
|
||||
vector.calculateEndPoint();
|
||||
|
||||
xDiff = vector.end.x - startX;
|
||||
yDiff = vector.end.y - startY;
|
||||
}
|
||||
else {
|
||||
// Zoom out to center
|
||||
xDiff = view.getCenterX() - startX;
|
||||
yDiff = view.getCenterY() - startY;
|
||||
}
|
||||
}
|
||||
|
||||
totalTime += time;
|
||||
|
||||
float ratio = (float) totalTime / (float) animationLengthMS;
|
||||
|
||||
if(ratio < 1) {
|
||||
|
||||
if(ratio > 0) {
|
||||
// we still have time left
|
||||
float newScale = (ratio * scaleDiff) + startScale;
|
||||
float newX = (ratio * xDiff) + startX;
|
||||
float newY = (ratio * yDiff) + startY;
|
||||
|
||||
if(zoomAnimationListener != null) {
|
||||
zoomAnimationListener.onZoom(newScale, newX, newY);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
float newScale = scaleDiff + startScale;
|
||||
float newX = xDiff + startX;
|
||||
float newY = yDiff + startY;
|
||||
|
||||
if(zoomAnimationListener != null) {
|
||||
zoomAnimationListener.onZoom(newScale, newX, newY);
|
||||
zoomAnimationListener.onComplete();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
firstFrame = true;
|
||||
totalTime = 0;
|
||||
}
|
||||
|
||||
public float getZoom() {
|
||||
return zoom;
|
||||
}
|
||||
|
||||
public void setZoom(float zoom) {
|
||||
this.zoom = zoom;
|
||||
}
|
||||
|
||||
public float getTouchX() {
|
||||
return touchX;
|
||||
}
|
||||
|
||||
public void setTouchX(float touchX) {
|
||||
this.touchX = touchX;
|
||||
}
|
||||
|
||||
public float getTouchY() {
|
||||
return touchY;
|
||||
}
|
||||
|
||||
public void setTouchY(float touchY) {
|
||||
this.touchY = touchY;
|
||||
}
|
||||
|
||||
public long getAnimationLengthMS() {
|
||||
return animationLengthMS;
|
||||
}
|
||||
|
||||
public void setAnimationLengthMS(long animationLengthMS) {
|
||||
this.animationLengthMS = animationLengthMS;
|
||||
}
|
||||
|
||||
public ZoomAnimationListener getZoomAnimationListener() {
|
||||
return zoomAnimationListener;
|
||||
}
|
||||
|
||||
public void setZoomAnimationListener(ZoomAnimationListener zoomAnimationListener) {
|
||||
this.zoomAnimationListener = zoomAnimationListener;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Jason Polites
|
||||
*
|
||||
* 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 com.polites.android;
|
||||
|
||||
|
||||
/**
|
||||
* @author Jason Polites
|
||||
*
|
||||
*/
|
||||
public interface ZoomAnimationListener {
|
||||
public void onZoom(float scale, float x, float y);
|
||||
public void onComplete();
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.wyx.pdf"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 23
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(include: ['*.jar'], dir: 'libs')
|
||||
testCompile 'junit:junit:4.12'
|
||||
compile 'com.android.support:appcompat-v7:23.1.1'
|
||||
compile project(':PdfView')
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id=":pdfviewsample" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="Android-Pdf-Viewer-Library" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
<option name="GRADLE_PROJECT_PATH" value=":pdfviewsample" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="debug" />
|
||||
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
|
||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
|
||||
<afterSyncTasks>
|
||||
<task>generateDebugSources</task>
|
||||
</afterSyncTasks>
|
||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
||||
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
|
||||
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
|
||||
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.1.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.1.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-support" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/restart-dex" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="support-v4-23.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-core-1.3" level="project" />
|
||||
<orderEntry type="library" exported="" scope="TEST" name="junit-4.12" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-annotations-23.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-23.1.1" level="project" />
|
||||
<orderEntry type="module" module-name="PdfView" exported="" />
|
||||
</component>
|
||||
</module>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/winney/Documents/android-sdk-macosx/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.wyx.pdfviewsample;
|
||||
|
||||
import android.app.Application;
|
||||
import android.test.ApplicationTestCase;
|
||||
|
||||
/**
|
||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||
*/
|
||||
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||
public ApplicationTest() {
|
||||
super(Application.class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.wyx.pdfviewsample"
|
||||
>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
>
|
||||
<activity android:name=".MainActivity">
|
||||
|
||||
</activity>
|
||||
<activity android:name=".Main2Activity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,242 @@
|
|||
package com.wyx.pdfviewsample;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Log record tool
|
||||
*/
|
||||
@SuppressWarnings({ "unused", "ResultOfMethodCallIgnored" }) public class L {
|
||||
|
||||
private static final int LOG_CAT_MAX_LENGTH = 3900;
|
||||
|
||||
private static final String TAG_LINE_BREAK = "****";
|
||||
private static final String EMPTY_LOG = "---";
|
||||
|
||||
private static final String ROOT = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
private static final String FILE_NAME = "logger.log";
|
||||
private static final int WRITE_TO_SD_PRIORITY_LEVEL = Log.DEBUG;
|
||||
|
||||
private static String logFile = ROOT + "/" + FILE_NAME;
|
||||
private static boolean write2SdCard = false;
|
||||
private static int write2SdPriorityLevel = WRITE_TO_SD_PRIORITY_LEVEL;
|
||||
|
||||
private static boolean debug = true;
|
||||
|
||||
public static void setDebug(boolean debug) {
|
||||
L.debug = debug;
|
||||
}
|
||||
|
||||
public static void setWrite2SdCard(boolean sdCard) {
|
||||
write2SdCard = sdCard;
|
||||
}
|
||||
|
||||
public static void setWriteToSdPriorityLevel(int level) {
|
||||
write2SdPriorityLevel = level;
|
||||
}
|
||||
|
||||
public static void exception(Throwable e) {
|
||||
if (debug && e != null) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void exception(Throwable e, String s) {
|
||||
if (debug && e != null) {
|
||||
e.printStackTrace();
|
||||
e(TAG_LINE_BREAK, s);
|
||||
}
|
||||
}
|
||||
|
||||
public static void w(Object object, Object msg) {
|
||||
if (debug) {
|
||||
print(Log.WARN, object, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void w(Object msg) {
|
||||
if (debug) {
|
||||
print(Log.WARN, TAG_LINE_BREAK, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void v(Object object, Object msg) {
|
||||
if (debug) {
|
||||
print(Log.VERBOSE, object, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void v(Object msg) {
|
||||
if (debug) {
|
||||
print(Log.VERBOSE, TAG_LINE_BREAK, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void d(Object object, Object msg) {
|
||||
if (debug) {
|
||||
print(Log.DEBUG, object, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void d(Object msg) {
|
||||
if (debug) {
|
||||
print(Log.DEBUG, TAG_LINE_BREAK, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void i(Object object, Object msg) {
|
||||
if (debug) {
|
||||
print(Log.INFO, object, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void i(Object msg) {
|
||||
if (debug) {
|
||||
print(Log.INFO, TAG_LINE_BREAK, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void e(Object object, Object msg) {
|
||||
if (debug) {
|
||||
print(Log.ERROR, object, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void e(Object msg) {
|
||||
if (debug) {
|
||||
print(Log.ERROR, TAG_LINE_BREAK, msg);
|
||||
}
|
||||
}
|
||||
|
||||
private static void print(int priority, Object tag, Object msg) {
|
||||
String s = toString(msg);
|
||||
printToLogCat(priority, tag, s);
|
||||
if (write2SdCard) {
|
||||
writeLog(priority, tag, s);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printToLogCat(int priority, Object tag, String s) {
|
||||
if (s.length() > LOG_CAT_MAX_LENGTH) {
|
||||
println(priority, tag, "log length - " + String.valueOf(s.length()));
|
||||
int chunkCount = s.length() / LOG_CAT_MAX_LENGTH; // integer division
|
||||
for (int i = 0; i <= chunkCount; i++) {
|
||||
int max = LOG_CAT_MAX_LENGTH * (i + 1);
|
||||
if (max >= s.length()) {
|
||||
println(priority, "chunk " + i + " of " + chunkCount, s.substring(LOG_CAT_MAX_LENGTH * i, s.length()));
|
||||
} else {
|
||||
println(priority, "chunk " + i + " of " + chunkCount, s.substring(LOG_CAT_MAX_LENGTH * i, max));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println(priority, tag, s);
|
||||
}
|
||||
}
|
||||
|
||||
public static void resetLogFile() {
|
||||
File file = new File(logFile);
|
||||
file.delete();
|
||||
try {
|
||||
file.createNewFile();
|
||||
} catch (IOException e) {
|
||||
exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeLog(int priority, Object tag, String s) {
|
||||
if (TextUtils.isEmpty(s)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (priority < write2SdPriorityLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
File file = new File(logFile);
|
||||
if (!file.exists()) {
|
||||
file.createNewFile();
|
||||
}
|
||||
FileWriter writer = new FileWriter(file, true);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void println(int priority, Object tag, String s) {
|
||||
Log.println(priority, getTagName(tag), s);
|
||||
}
|
||||
|
||||
private static String getTagName(Object tag) {
|
||||
if (tag instanceof String) {
|
||||
return (String) tag;
|
||||
}
|
||||
|
||||
if (tag instanceof Class<?>) {
|
||||
return ((Class<?>) tag).getSimpleName();
|
||||
} else {
|
||||
return getTagName(tag.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private static String toString(Object msg) {
|
||||
if (msg == null) {
|
||||
return EMPTY_LOG;
|
||||
}
|
||||
String s = msg.toString();
|
||||
if (s.isEmpty()) {
|
||||
return EMPTY_LOG;
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
public static void printTouchEvent(MotionEvent ev) {
|
||||
L.e("touch event", actionToString(ev.getAction()));
|
||||
final int pointerCount = ev.getPointerCount();
|
||||
for (int i = 0; i < pointerCount; i++) {
|
||||
L.d("point",
|
||||
"id[" + i + "]=" + ev.getPointerId(i) + ", x[" + i + "]=" + ev.getX(i) + ", y[" + i + "]=" + ev.getY(i));
|
||||
}
|
||||
// L.d("pointer count", pointerCount);
|
||||
}
|
||||
|
||||
public static String actionToString(int action) {
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
return "ACTION_DOWN";
|
||||
case MotionEvent.ACTION_UP:
|
||||
return "ACTION_UP";
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
return "ACTION_CANCEL";
|
||||
case MotionEvent.ACTION_OUTSIDE:
|
||||
return "ACTION_OUTSIDE";
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
return "ACTION_MOVE";
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
return "ACTION_HOVER_MOVE";
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
return "ACTION_SCROLL";
|
||||
case MotionEvent.ACTION_HOVER_ENTER:
|
||||
return "ACTION_HOVER_ENTER";
|
||||
case MotionEvent.ACTION_HOVER_EXIT:
|
||||
return "ACTION_HOVER_EXIT";
|
||||
}
|
||||
int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
switch (action & MotionEvent.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
return "ACTION_POINTER_DOWN(" + index + ")";
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
return "ACTION_POINTER_UP(" + index + ")";
|
||||
default:
|
||||
return Integer.toString(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.wyx.pdfviewsample;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import java.io.IOException;
|
||||
import net.sf.andpdf.pdfviewer.gui.PdfView;
|
||||
import net.sf.andpdf.utils.FileUtils;
|
||||
|
||||
public class Main2Activity extends Activity {
|
||||
|
||||
PdfView pdfView;
|
||||
|
||||
@Override protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main2);
|
||||
|
||||
pdfView = (PdfView) findViewById(R.id.pdf_view);
|
||||
|
||||
//ViewGroup.LayoutParams params =
|
||||
// new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
//GestureImageView view = pdfView.mImageView;
|
||||
//view.setImageResource(R.drawable.back02);
|
||||
//view.setLayoutParams(params);
|
||||
|
||||
//ViewGroup layout = (ViewGroup) findViewById(R.id.layout);
|
||||
|
||||
//layout.addView(view);
|
||||
View view = pdfView.mImageView;
|
||||
}
|
||||
|
||||
@Override protected void onStart() {
|
||||
super.onStart();
|
||||
try {
|
||||
pdfView.parsePDF(FileUtils.fileFromAsset(this, "sample.pdf"), null);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
pdfView.startRenderThread(1, 1.0f);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package com.wyx.pdfviewsample;
|
||||
|
||||
import java.io.IOException;
|
||||
import net.sf.andpdf.pdfviewer.PdfViewerActivity;
|
||||
import net.sf.andpdf.utils.FileUtils;
|
||||
|
||||
public class MainActivity extends PdfViewerActivity {
|
||||
|
||||
@Override public String getFileName() {
|
||||
try {
|
||||
return FileUtils.fileFromAsset(this, "about.pdf").toString();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public int getPreviousPageImageResource() {
|
||||
return R.drawable.left_arrow;
|
||||
}
|
||||
|
||||
@Override public int getNextPageImageResource() {
|
||||
return R.drawable.right_arrow;
|
||||
}
|
||||
|
||||
@Override public int getZoomInImageResource() {
|
||||
return R.drawable.zoom_in;
|
||||
}
|
||||
|
||||
@Override public int getZoomOutImageResource() {
|
||||
return R.drawable.zoom_out;
|
||||
}
|
||||
|
||||
@Override public int getPdfPasswordLayoutResource() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public int getPdfPageNumberResource() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public int getPdfPasswordEditField() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public int getPdfPasswordOkButton() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public int getPdfPasswordExitButton() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public int getPdfPageNumberEditField() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
tools:context="com.wyx.pdfviewsample.MainActivity"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:text="Hello World!"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
</RelativeLayout>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/layout"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<net.sf.andpdf.pdfviewer.gui.PdfView
|
||||
android:id="@+id/pdf_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="20dp"
|
||||
/>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
|
|
@ -0,0 +1,6 @@
|
|||
<resources>
|
||||
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||
(such as screen margins) for screens with more than 820dp of available width. This
|
||||
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">PdfViewSample</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.wyx.pdfviewsample;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* To work on unit tests, switch the Test Artifact in the Build Variants view.
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test public void addition_isCorrect() throws Exception {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
include ':PdfView'
|
||||
include ':PdfView', ':pdfviewsample'
|
||||
include ':gestureimageview'
|
||||
|
|
|
|||
Loading…
Reference in New Issue