1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-03 17:59:33 +02:00

directory renaming -- preparing for gradle build system

This commit is contained in:
Nikolay Pultsin 2015-09-29 15:54:34 +01:00
parent e672bf6892
commit 2c1d5c53e2
660 changed files with 1 additions and 0 deletions

View file

@ -0,0 +1,187 @@
/*
* Copyright (C) 2013 Paragon Software Group
* Author: Andrey Tumanov <Andrey_Tumanov@penreader.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package com.paragon.dictionary.fbreader;
import java.io.FileOutputStream;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.WebView;
import android.widget.*;
import com.paragon.open.dictionary.api.*;
import org.geometerplus.zlibrary.core.util.MiscUtil;
import org.geometerplus.zlibrary.ui.android.R;
public class OpenDictionaryActivity extends Activity {
public final static String OPEN_DICTIONARY_QUERY_KEY = "open_dictionary_query";
public final static String OPEN_DICTIONARY_HEIGHT_KEY = "open_dictionary_height";
public final static String OPEN_DICTIONARY_GRAVITY_KEY = "open_dictionary_gravity";
private WebView myArticleView = null;
private TextView myTitleLabel = null;
private ImageButton myOpenDictionaryButton = null;
private String myQuery = null;
private int myHeight;
private int myGravity;
private static Dictionary ourDictionary = null;
static void setDictionary(Dictionary dictionary) {
ourDictionary = dictionary;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.opendictionary_flyout);
myArticleView = (WebView) findViewById(R.id.opendictionary_article_view);
myTitleLabel = (TextView) findViewById(R.id.opendictionary_title_label);
myOpenDictionaryButton = (ImageButton) findViewById(R.id.opendictionary_open_button);
myOpenDictionaryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
openTextInDictionary(myQuery);
}
});
}
@Override
protected void onResume() {
super.onResume();
myQuery = getIntent().getStringExtra(OPEN_DICTIONARY_QUERY_KEY);
if (myQuery == null)
myQuery = "";
myHeight = getIntent().getIntExtra(OPEN_DICTIONARY_HEIGHT_KEY, -1);
myGravity = getIntent().getIntExtra(OPEN_DICTIONARY_GRAVITY_KEY, android.view.Gravity.BOTTOM);
setViewSize(myHeight, myGravity);
myArticleView.loadData("", "text/text", "UTF-8");
if (ourDictionary != null) {
myTitleLabel.setText(ourDictionary.getName());
Log.d("FBReader", "OpenDictionaryActivity: get translation as text");
ourDictionary.getTranslationAsText(myQuery, TranslateMode.SHORT, TranslateFormat.HTML, new Dictionary.TranslateAsTextListener() {
@Override
public void onComplete(String s, TranslateMode translateMode) {
final String url = saveArticle(s.replace("</BODY>", "<br><br></BODY>"), getApplicationContext());
if (MiscUtil.isEmptyString(url)) {
openTextInDictionary(myQuery);
} else {
myArticleView.loadUrl(url);
}
Log.d("FBReader", "OpenDictionaryActivity: translation ready");
}
@Override
public void onWordNotFound(ArrayList<String> similarWords) {
finish();
openTextInDictionary(myQuery);
Log.d("FBReader", "OpenDictionaryActivity: word not found");
}
@Override
public void onError(com.paragon.open.dictionary.api.Error error) {
finish();
Log.e("FBReader", error.getName());
Log.e("FBReader", error.getMessage());
}
@Override
public void onIPCError(String s) {
finish();
Log.e("FBReader", s);
}
});
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setViewSize(myHeight, myGravity);
}
private void setViewSize(int height, int gravity) {
final DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
if (height < 0)
height = metrics.heightPixels / 3;
TableRow bottomRow = (TableRow)findViewById(R.id.bottom_row);
TableRow topRow = (TableRow)findViewById(R.id.top_row);
View.OnTouchListener touchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
finish();
return false;
}
};
topRow.setOnTouchListener(touchListener);
bottomRow.setOnTouchListener(touchListener);
switch (gravity) {
case android.view.Gravity.TOP:
topRow.setMinimumHeight(0);
bottomRow.setMinimumHeight(metrics.heightPixels - height);
break;
case android.view.Gravity.BOTTOM:
default:
bottomRow.setMinimumHeight(0);
topRow.setMinimumHeight(metrics.heightPixels - height);
break;
}
}
private void openTextInDictionary(String text) {
if (ourDictionary != null)
ourDictionary.showTranslation(text);
}
private String saveArticle(String data, Context context) {
final String filename = "open_dictionary_article.html";
final FileOutputStream outputStream;
try {
outputStream = context.openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write(data.getBytes());
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
return null;
}
return "file://" + context.getFilesDir().getAbsolutePath() + "/" + filename;
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (C) 2013 Paragon Software Group
* Author: Andrey Tumanov <Andrey_Tumanov@penreader.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package com.paragon.dictionary.fbreader;
import java.util.HashSet;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.paragon.open.dictionary.api.*;
import org.geometerplus.android.fbreader.dict.DictionaryUtil;
public class OpenDictionaryFlyout {
private final Direction myDirection;
private final String myPackageName;
public OpenDictionaryFlyout(Dictionary dictionary) {
myDirection = dictionary.getDirection();
myPackageName = dictionary.getApplicationPackageName();
}
private Dictionary getDictionary(final Context context) {
if (myPackageName == null) {
return null;
}
final OpenDictionaryAPI api = new OpenDictionaryAPI(context);
HashSet<Dictionary> dictionaries = api.getDictionaries(myDirection);
for (Dictionary dictionary : dictionaries) {
if (myPackageName.equalsIgnoreCase(dictionary.getApplicationPackageName())) {
return dictionary;
}
}
Log.e("FBReader", "OpenDictionaryFlyout:getDictionary - Dictionary with direction [" +
myDirection.toString() + "] and package name [" + myPackageName + "] not found");
return null;
}
public void showTranslation(final Activity activity, final String text, DictionaryUtil.PopupFrameMetric frameMetrics) {
Log.d("FBReader", "OpenDictionaryFlyout:showTranslation");
final Dictionary dictionary = getDictionary(activity);
if (dictionary == null) {
Log.e("FBReader", "OpenDictionaryFlyout:showTranslation - null dictionary received");
return;
}
if (!dictionary.isTranslationAsTextSupported()) {
dictionary.showTranslation(text);
} else {
OpenDictionaryActivity.setDictionary(dictionary);
Intent intent = new Intent(activity, OpenDictionaryActivity.class);
intent.putExtra(OpenDictionaryActivity.OPEN_DICTIONARY_QUERY_KEY, text);
intent.putExtra(OpenDictionaryActivity.OPEN_DICTIONARY_HEIGHT_KEY, frameMetrics.Height);
intent.putExtra(OpenDictionaryActivity.OPEN_DICTIONARY_GRAVITY_KEY, frameMetrics.Gravity);
activity.startActivity(intent);
}
}
}

View file

@ -0,0 +1,51 @@
package org.amse.ys.zip;
import java.util.*;
import java.io.*;
public abstract class Decompressor {
public Decompressor(MyBufferedInputStream is, LocalFileHeader header) {
}
/**
* byte b[] -- target buffer for bytes; might be null
*/
public abstract int read(byte b[], int off, int len) throws IOException;
public abstract int read() throws IOException;
protected Decompressor() {
}
private static Queue<DeflatingDecompressor> ourDeflators = new LinkedList<DeflatingDecompressor>();
static void storeDecompressor(Decompressor decompressor) {
if (decompressor instanceof DeflatingDecompressor) {
synchronized (ourDeflators) {
ourDeflators.add((DeflatingDecompressor)decompressor);
}
}
}
static Decompressor init(MyBufferedInputStream is, LocalFileHeader header) throws IOException {
switch (header.CompressionMethod) {
case 0:
return new NoCompressionDecompressor(is, header);
case 8:
synchronized (ourDeflators) {
if (!ourDeflators.isEmpty()) {
DeflatingDecompressor decompressor = ourDeflators.poll();
decompressor.reset(is, header);
return decompressor;
}
}
return new DeflatingDecompressor(is, header);
default:
throw new ZipException("Unsupported method of compression");
}
}
public int available() throws IOException {
return -1;
}
}

View file

@ -0,0 +1,166 @@
package org.amse.ys.zip;
import java.io.*;
class DeflatingDecompressor extends Decompressor {
static {
System.loadLibrary("DeflatingDecompressor-v3");
}
// common variables
private MyBufferedInputStream myStream;
private int myCompressedAvailable;
private int myAvailable;
private static final int IN_BUFFER_SIZE = 2048;
private static final int OUT_BUFFER_SIZE = 32768;
private final byte[] myInBuffer = new byte[IN_BUFFER_SIZE];
private int myInBufferOffset;
private int myInBufferLength;
private final byte[] myOutBuffer = new byte[OUT_BUFFER_SIZE];
private int myOutBufferOffset;
private int myOutBufferLength;
private volatile int myInflatorId = -1;
public DeflatingDecompressor(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException {
super();
reset(inputStream, header);
}
void reset(MyBufferedInputStream inputStream, LocalFileHeader header) throws IOException {
if (myInflatorId != -1) {
endInflating(myInflatorId);
myInflatorId = -1;
}
myStream = inputStream;
myCompressedAvailable = header.CompressedSize;
if (myCompressedAvailable <= 0) {
myCompressedAvailable = Integer.MAX_VALUE;
}
myAvailable = header.UncompressedSize;
if (myAvailable <= 0) {
myAvailable = Integer.MAX_VALUE;
}
myInBufferOffset = IN_BUFFER_SIZE;
myInBufferLength = 0;
myOutBufferOffset = OUT_BUFFER_SIZE;
myOutBufferLength = 0;
myInflatorId = startInflating();
if (myInflatorId == -1) {
throw new ZipException("cannot start inflating");
}
}
@Override
public int available() {
return myAvailable;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (myAvailable <= 0) {
return -1;
}
if (len > myAvailable) {
len = myAvailable;
}
for (int toFill = len; toFill > 0; ) {
if (myOutBufferLength == 0) {
fillOutBuffer();
}
if (myOutBufferLength == 0) {
len -= toFill;
break;
}
final int ready = (toFill < myOutBufferLength) ? toFill : myOutBufferLength;
if (b != null) {
System.arraycopy(myOutBuffer, myOutBufferOffset, b, off, ready);
}
off += ready;
myOutBufferOffset += ready;
toFill -= ready;
myOutBufferLength -= ready;
}
if (len > 0) {
myAvailable -= len;
} else {
myAvailable = 0;
}
return len;
}
@Override
public int read() throws IOException {
if (myAvailable <= 0) {
return -1;
}
if (myOutBufferLength == 0) {
fillOutBuffer();
}
if (myOutBufferLength == 0) {
myAvailable = 0;
return -1;
}
--myAvailable;
--myOutBufferLength;
return myOutBuffer[myOutBufferOffset++];
}
private void fillOutBuffer() throws IOException {
if (myInflatorId == -1) {
return;
}
while (myOutBufferLength == 0) {
if (myInBufferLength == 0) {
myInBufferOffset = 0;
final int toRead = (myCompressedAvailable < IN_BUFFER_SIZE) ? myCompressedAvailable : IN_BUFFER_SIZE;
myInBufferLength = myStream.read(myInBuffer, 0, toRead);
if (myInBufferLength < toRead) {
myCompressedAvailable = 0;
} else {
myCompressedAvailable -= toRead;
}
}
if (myInBufferLength <= 0) {
break;
}
final long result = inflate(myInflatorId, myInBuffer, myInBufferOffset, myInBufferLength, myOutBuffer);
if (result <= 0) {
final StringBuilder extraInfo = new StringBuilder()
.append(myStream.offset()).append(":")
.append(myInBufferOffset).append(":")
.append(myInBufferLength).append(":")
.append(myOutBuffer.length).append(":");
for (int i = 0; i < Math.min(10, myInBufferLength); ++i) {
extraInfo.append(myInBuffer[myInBufferOffset + i]).append(",");
}
throw new ZipException("Cannot inflate zip-compressed block, code = " + result + ";extra info = " + extraInfo);
}
final int in = (int)(result >> 16) & 0xFFFF;
if (in > myInBufferLength) {
throw new ZipException("Invalid inflating result, code = " + result + "; buffer length = " + myInBufferLength);
}
final int out = (int)result & 0xFFFF;
myInBufferOffset += in;
myInBufferLength -= in;
myOutBufferOffset = 0;
myOutBufferLength = out;
if ((result & (1L << 32)) != 0) {
endInflating(myInflatorId);
myInflatorId = -1;
myStream.backSkip(myInBufferLength);
break;
}
}
}
private native int startInflating();
private native void endInflating(int inflatorId);
private native long inflate(int inflatorId, byte[] in, int inOffset, int inLength, byte[] out);
}

View file

@ -0,0 +1,94 @@
package org.amse.ys.zip;
/**
* Class consists of constants, describing a compressed file. Contains only
* construcor, all fields are final.
*/
import java.io.IOException;
public class LocalFileHeader {
static final int FILE_HEADER_SIGNATURE = 0x04034b50;
static final int FOLDER_HEADER_SIGNATURE = 0x02014b50;
static final int END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50;
static final int DATA_DESCRIPTOR_SIGNATURE = 0x08074b50;
int Signature;
int Version;
int Flags;
int CompressionMethod;
int ModificationTime;
int ModificationDate;
int CRC32;
int CompressedSize;
int UncompressedSize;
int NameLength;
int ExtraLength;
public String FileName;
int DataOffset;
LocalFileHeader() {
}
void readFrom(MyBufferedInputStream stream) throws IOException {
Signature = stream.read4Bytes();
switch (Signature) {
default:
break;
case END_OF_CENTRAL_DIRECTORY_SIGNATURE:
{
stream.skip(16);
int comment = stream.read2Bytes();
stream.skip(comment);
break;
}
case FOLDER_HEADER_SIGNATURE:
{
Version = stream.read4Bytes();
Flags = stream.read2Bytes();
CompressionMethod = stream.read2Bytes();
ModificationTime = stream.read2Bytes();
ModificationDate = stream.read2Bytes();
CRC32 = stream.read4Bytes();
CompressedSize = stream.read4Bytes();
UncompressedSize = stream.read4Bytes();
if (CompressionMethod == 0 && CompressedSize != UncompressedSize) {
CompressedSize = UncompressedSize;
}
NameLength = stream.read2Bytes();
ExtraLength = stream.read2Bytes();
int comment = stream.read2Bytes();
stream.skip(12);
FileName = stream.readString(NameLength);
stream.skip(ExtraLength);
stream.skip(comment);
break;
}
case FILE_HEADER_SIGNATURE:
Version = stream.read2Bytes();
Flags = stream.read2Bytes();
CompressionMethod = stream.read2Bytes();
ModificationTime = stream.read2Bytes();
ModificationDate = stream.read2Bytes();
CRC32 = stream.read4Bytes();
CompressedSize = stream.read4Bytes();
UncompressedSize = stream.read4Bytes();
if (CompressionMethod == 0 && CompressedSize != UncompressedSize) {
CompressedSize = UncompressedSize;
}
NameLength = stream.read2Bytes();
ExtraLength = stream.read2Bytes();
FileName = stream.readString(NameLength);
stream.skip(ExtraLength);
break;
case DATA_DESCRIPTOR_SIGNATURE:
CRC32 = stream.read4Bytes();
CompressedSize = stream.read4Bytes();
UncompressedSize = stream.read4Bytes();
break;
}
DataOffset = stream.offset();
}
}

View file

@ -0,0 +1,164 @@
package org.amse.ys.zip;
import java.io.*;
import org.geometerplus.zlibrary.core.util.InputStreamHolder;
final class MyBufferedInputStream extends InputStream {
private final InputStreamHolder myStreamHolder;
private InputStream myFileInputStream;
private final byte[] myBuffer;
int myBytesReady;
int myPositionInBuffer;
private int myCurrentPosition;
public MyBufferedInputStream(InputStreamHolder streamHolder, int bufferSize) throws IOException {
myStreamHolder = streamHolder;
myFileInputStream = streamHolder.getInputStream();
myBuffer = new byte[bufferSize];
myBytesReady = 0;
myPositionInBuffer = 0;
}
public MyBufferedInputStream(InputStreamHolder streamHolder) throws IOException {
this(streamHolder, 1 << 10);
}
@Override
public int available() throws IOException {
return (myFileInputStream.available() + myBytesReady);
}
int offset() {
return myCurrentPosition;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int ready = (len < myBytesReady) ? len : myBytesReady;
if (ready > 0) {
if (b != null) {
System.arraycopy(myBuffer, myPositionInBuffer, b, off, ready);
}
len -= ready;
myBytesReady -= ready;
myPositionInBuffer += ready;
off += ready;
}
if (len > 0) {
final int ready2 = myFileInputStream.read(b, off, len);
if (ready2 >= 0) {
ready += ready2;
}
}
myCurrentPosition += ready;
return ready > 0 ? ready : -1;
}
@Override
public int read() throws IOException {
myCurrentPosition++;
if (myBytesReady <= 0) {
myPositionInBuffer = 0;
myBytesReady = myFileInputStream.read(myBuffer);
if (myBytesReady <= 0) {
return -1;
}
}
myBytesReady--;
return myBuffer[myPositionInBuffer++] & 255;
}
int read2Bytes() throws IOException {
int low = read();
int high = read();
if (high < 0) {
throw new ZipException("unexpected end of file at position " + offset());
}
return (high << 8) + low;
}
int read4Bytes() throws IOException {
int firstByte = read();
int secondByte = read();
int thirdByte = read();
int fourthByte = read();
if (fourthByte < 0) {
throw new ZipException("unexpected end of file at position " + offset());
}
return (fourthByte << 24) + (thirdByte << 16) + (secondByte << 8) + firstByte;
}
String readString(int stringLength) throws IOException {
char[] array = new char[stringLength];
for (int i = 0; i < stringLength; i++) {
array[i] = (char)read();
}
return new String(array);
}
@Override
public long skip(long n) throws IOException {
if (myBytesReady >= n) {
myBytesReady -= n;
myPositionInBuffer += n;
myCurrentPosition += n;
return n;
} else {
long left = n - myBytesReady;
myBytesReady = 0;
left -= myFileInputStream.skip(left);
while (left > 0) {
int skipped = myFileInputStream.read(myBuffer, 0, Math.min((int)left, myBuffer.length));
if (skipped <= 0) {
break;
}
left -= skipped;
}
myCurrentPosition += n - left;
return n - left;
}
}
public void backSkip(int n) throws IOException {
if (n <= 0) {
return;
}
myFileInputStream.close();
myFileInputStream = myStreamHolder.getInputStream();
myBytesReady = 0;
myPositionInBuffer = 0;
int position = myCurrentPosition - n;
myCurrentPosition = 0;
skip(position);
}
public void setPosition(int position) throws IOException {
if (myCurrentPosition < position) {
skip(position - myCurrentPosition);
} else {
backSkip(myCurrentPosition - position);
}
}
/*
public void setPosition(int position) throws IOException {
if (myCurrentPosition < position) {
skip(position - myCurrentPosition);
} else {
myFileInputStream.close();
myFileInputStream = myStreamHolder.getInputStream();
myBytesReady = 0;
skip(position);
myCurrentPosition = position;
}
}
*/
@Override
public void close() throws IOException {
myFileInputStream.close();
myBytesReady = 0;
}
}

View file

@ -0,0 +1,44 @@
package org.amse.ys.zip;
import java.io.*;
public final class NoCompressionDecompressor extends Decompressor {
private final LocalFileHeader myHeader;
private final MyBufferedInputStream myStream;
private int myCurrentPosition;
public NoCompressionDecompressor(MyBufferedInputStream is, LocalFileHeader header) {
super();
myHeader = header;
myStream = is;
}
@Override
public int read(byte b[], int off, int len) throws IOException {
final int left = available();
if (left <= 0) {
return -1;
}
if (len > left) {
len = left;
}
final int r = myStream.read(b, off, len);
myCurrentPosition += r;
return r;
}
@Override
public int read() throws IOException {
if (myCurrentPosition < myHeader.CompressedSize) {
myCurrentPosition++;
return myStream.read();
} else {
return -1;
}
}
@Override
public int available() throws IOException {
return myHeader.UncompressedSize - myCurrentPosition;
}
}

View file

@ -0,0 +1,10 @@
package org.amse.ys.zip;
import java.io.IOException;
@SuppressWarnings("serial")
public class ZipException extends IOException {
ZipException(String message) {
super(message);
}
}

View file

@ -0,0 +1,150 @@
package org.amse.ys.zip;
import java.io.*;
import java.util.*;
import org.geometerplus.zlibrary.core.util.InputStreamHolder;
public final class ZipFile {
private final static Comparator<String> ourIgnoreCaseComparator = new Comparator<String>() {
@Override
public final int compare(String s0, String s1) {
return s0.compareToIgnoreCase(s1);
}
};
private final InputStreamHolder myStreamHolder;
private final Map<String,LocalFileHeader> myFileHeaders =
new TreeMap<String,LocalFileHeader>(ourIgnoreCaseComparator);
private boolean myAllFilesAreRead;
public ZipFile(InputStreamHolder streamHolder) {
myStreamHolder = streamHolder;
}
public Collection<LocalFileHeader> headers() {
try {
readAllHeaders();
} catch (IOException e) {
}
return myFileHeaders.values();
}
private boolean readFileHeader(MyBufferedInputStream baseStream, String fileToFind) throws IOException {
LocalFileHeader header = new LocalFileHeader();
header.readFrom(baseStream);
if (header.Signature != LocalFileHeader.FILE_HEADER_SIGNATURE) {
return false;
}
if (header.FileName != null) {
myFileHeaders.put(header.FileName, header);
if (header.FileName.equalsIgnoreCase(fileToFind)) {
return true;
}
}
if ((header.Flags & 0x08) == 0) {
baseStream.skip(header.CompressedSize);
} else {
findAndReadDescriptor(baseStream, header);
}
return false;
}
private void readAllHeaders() throws IOException {
if (myAllFilesAreRead) {
return;
}
myAllFilesAreRead = true;
MyBufferedInputStream baseStream = getBaseStream();
baseStream.setPosition(0);
myFileHeaders.clear();
try {
while (baseStream.available() > 0) {
readFileHeader(baseStream, null);
}
} finally {
storeBaseStream(baseStream);
}
}
/**
* Finds descriptor of the last header and installs sizes of files
*/
private void findAndReadDescriptor(MyBufferedInputStream baseStream, LocalFileHeader header) throws IOException {
final Decompressor decompressor = Decompressor.init(baseStream, header);
int uncompressedSize = 0;
while (true) {
int blockSize = decompressor.read(null, 0, 2048);
if (blockSize <= 0) {
break;
}
uncompressedSize += blockSize;
}
header.UncompressedSize = uncompressedSize;
Decompressor.storeDecompressor(decompressor);
}
private final Queue<MyBufferedInputStream> myStoredStreams = new LinkedList<MyBufferedInputStream>();
synchronized void storeBaseStream(MyBufferedInputStream baseStream) {
myStoredStreams.add(baseStream);
}
synchronized MyBufferedInputStream getBaseStream() throws IOException {
final MyBufferedInputStream stored = myStoredStreams.poll();
if (stored != null) {
return stored;
}
return new MyBufferedInputStream(myStreamHolder);
}
private ZipInputStream createZipInputStream(LocalFileHeader header) throws IOException {
return new ZipInputStream(this, header);
}
public boolean entryExists(String entryName) {
try {
return getHeader(entryName) != null;
} catch (IOException e) {
return false;
}
}
public int getEntrySize(String entryName) throws IOException {
return getHeader(entryName).UncompressedSize;
}
public InputStream getInputStream(String entryName) throws IOException {
return createZipInputStream(getHeader(entryName));
}
public LocalFileHeader getHeader(String entryName) throws IOException {
if (!myFileHeaders.isEmpty()) {
LocalFileHeader header = myFileHeaders.get(entryName);
if (header != null) {
return header;
}
if (myAllFilesAreRead) {
throw new ZipException("Entry " + entryName + " is not found");
}
}
// ready to read file header
MyBufferedInputStream baseStream = getBaseStream();
baseStream.setPosition(0);
try {
while (baseStream.available() > 0 && !readFileHeader(baseStream, entryName)) {
}
final LocalFileHeader header = myFileHeaders.get(entryName);
if (header != null) {
return header;
}
} finally {
storeBaseStream(baseStream);
}
throw new ZipException("Entry " + entryName + " is not found");
}
}

View file

@ -0,0 +1,58 @@
package org.amse.ys.zip;
import java.io.*;
class ZipInputStream extends InputStream {
private final ZipFile myParent;
private final MyBufferedInputStream myBaseStream;
private final Decompressor myDecompressor;
private boolean myIsClosed;
public ZipInputStream(ZipFile parent, LocalFileHeader header) throws IOException {
myParent = parent;
myBaseStream = parent.getBaseStream();
myBaseStream.setPosition(header.DataOffset);
myDecompressor = Decompressor.init(myBaseStream, header);
}
@Override
public int available() throws IOException {
return myDecompressor.available();
}
@Override
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || off + len > b.length) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
return myDecompressor.read(b, off, len);
}
@Override
public int read() throws IOException {
return myDecompressor.read();
}
@Override
public void close() throws IOException {
if (!myIsClosed) {
myIsClosed = true;
myParent.storeBaseStream(myBaseStream);
Decompressor.storeDecompressor(myDecompressor);
}
}
@Override
protected void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
}

View file

@ -0,0 +1,113 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import java.util.ArrayList;
import android.app.*;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.zlibrary.ui.android.network.SQLiteCookieDatabase;
import org.geometerplus.fbreader.Paths;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.fbreader.network.NetworkImage;
import org.geometerplus.fbreader.network.sync.SyncData;
import org.geometerplus.fbreader.network.urlInfo.UrlInfo;
import org.geometerplus.android.fbreader.network.BookDownloaderService;
import org.geometerplus.android.fbreader.sync.MissingBookActivity;
class AppNotifier implements FBReaderApp.Notifier {
private final Activity myActivity;
private final ArrayList<String> myLatestHashes = new ArrayList<String>();
private volatile long myLatestNotificationStamp;
AppNotifier(Activity activity) {
myActivity = activity;
}
@Override
public void showMissingBookNotification(final SyncData.ServerBookInfo info) {
synchronized (this) {
myLatestHashes.retainAll(info.Hashes);
if (!myLatestHashes.isEmpty() &&
myLatestNotificationStamp > System.currentTimeMillis() - 5 * 60 * 1000) {
return;
}
myLatestHashes.addAll(info.Hashes);
myLatestNotificationStamp = System.currentTimeMillis();
}
new Thread() {
public void run() {
showMissingBookNotificationInternal(info);
}
}.start();
}
private void showMissingBookNotificationInternal(SyncData.ServerBookInfo info) {
final String errorTitle = MissingBookActivity.errorTitle();
final NotificationManager notificationManager =
(NotificationManager)myActivity.getSystemService(Activity.NOTIFICATION_SERVICE);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(myActivity)
.setSmallIcon(R.drawable.fbreader)
.setTicker(errorTitle)
.setContentTitle(errorTitle)
.setContentText(info.Title)
.setAutoCancel(false);
if (info.ThumbnailUrl != null) {
SQLiteCookieDatabase.init(myActivity);
final NetworkImage thumbnail = new NetworkImage(info.ThumbnailUrl, Paths.systemInfo(myActivity));
thumbnail.synchronize();
try {
builder.setLargeIcon(
BitmapFactory.decodeStream(thumbnail.getRealImage().inputStream())
);
} catch (Throwable t) {
// ignore
}
}
Uri uri = null;
try {
uri = Uri.parse(info.DownloadUrl);
} catch (Exception e) {
}
if (uri != null) {
final Intent downloadIntent = new Intent(myActivity, MissingBookActivity.class);
downloadIntent
.setData(uri)
.putExtra(BookDownloaderService.Key.FROM_SYNC, true)
.putExtra(BookDownloaderService.Key.BOOK_MIME, info.Mimetype)
.putExtra(BookDownloaderService.Key.BOOK_KIND, UrlInfo.Type.Book)
.putExtra(BookDownloaderService.Key.BOOK_TITLE, info.Title);
builder.setContentIntent(PendingIntent.getActivity(myActivity, 0, downloadIntent, 0));
} else {
builder.setContentIntent(PendingIntent.getActivity(myActivity, 0, new Intent(), 0));
}
notificationManager.notify(NotificationUtil.MISSING_BOOK_ID, builder.build());
}
}

View file

@ -0,0 +1,133 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import java.util.List;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.*;
import android.view.*;
import org.geometerplus.zlibrary.core.options.Config;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.fbreader.fbreader.options.CancelMenuHelper;
import org.geometerplus.android.fbreader.api.FBReaderIntents;
import org.geometerplus.android.fbreader.libraryService.BookCollectionShadow;
import org.geometerplus.android.util.ViewUtil;
public class CancelActivity extends ListActivity {
private BookCollectionShadow myCollection;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
@Override
protected void onStart() {
super.onStart();
// we use this local variable to be sure collection is not null inside the runnable
final BookCollectionShadow collection = new BookCollectionShadow();
myCollection = collection;
collection.bindToService(this, new Runnable() {
public void run() {
final ActionListAdapter adapter = new ActionListAdapter(
new CancelMenuHelper().getActionsList(collection)
);
setListAdapter(adapter);
getListView().setOnItemClickListener(adapter);
}
});
}
@Override
protected void onStop() {
if (myCollection != null) {
myCollection.unbind();
myCollection = null;
}
super.onStop();
}
private class ActionListAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
private final List<CancelMenuHelper.ActionDescription> myActions;
ActionListAdapter(List<CancelMenuHelper.ActionDescription> actions) {
myActions = actions;
}
public final int getCount() {
return myActions.size();
}
public final CancelMenuHelper.ActionDescription getItem(int position) {
return myActions.get(position);
}
public final long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, final ViewGroup parent) {
final View view = convertView != null
? convertView
: LayoutInflater.from(parent.getContext()).inflate(R.layout.cancel_item, parent, false);
final CancelMenuHelper.ActionDescription item = getItem(position);
final TextView titleView = ViewUtil.findTextView(view, R.id.cancel_item_title);
final TextView summaryView = ViewUtil.findTextView(view, R.id.cancel_item_summary);
final String title = item.Title;
final String summary = item.Summary;
titleView.setText(title);
if (summary != null) {
summaryView.setVisibility(View.VISIBLE);
summaryView.setText(summary);
titleView.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT
));
} else {
summaryView.setVisibility(View.GONE);
titleView.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT
));
}
return view;
}
public final void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final Intent data = new Intent();
final CancelMenuHelper.ActionDescription item = getItem(position);
data.putExtra(FBReaderIntents.Key.TYPE, item.Type.name());
if (item instanceof CancelMenuHelper.BookmarkDescription) {
FBReaderIntents.putBookmarkExtra(
data, ((CancelMenuHelper.BookmarkDescription)item).Bookmark
);
}
setResult(RESULT_FIRST_USER, data);
finish();
}
}
}

View file

@ -0,0 +1,193 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import java.io.File;
import java.util.List;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.PopupWindow;
import android.widget.*;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.zlibrary.core.network.QuietNetworkContext;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.text.view.*;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.zlibrary.ui.android.image.ZLAndroidImageData;
import org.geometerplus.fbreader.book.Book;
import org.geometerplus.fbreader.fbreader.BookElement;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.fbreader.fbreader.options.ColorProfile;
import org.geometerplus.fbreader.network.opds.OPDSBookItem;
import org.geometerplus.fbreader.network.urlInfo.BookUrlInfo;
import org.geometerplus.fbreader.network.urlInfo.UrlInfo;
import org.geometerplus.android.util.UIMessageUtil;
import org.geometerplus.android.util.UIUtil;
class DisplayBookPopupAction extends FBAndroidAction {
DisplayBookPopupAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
private void openBook(final PopupWindow popup, final Book book) {
if (book == null) {
return;
}
BaseActivity.runOnUiThread(new Runnable() {
public void run() {
popup.dismiss();
Reader.openBook(book, null, null, null);
}
});
}
@Override
protected void run(Object ... params) {
if (params.length != 1 || !(params[0] instanceof ZLTextRegion)) {
return;
}
final ZLTextRegion region = (ZLTextRegion)params[0];
if (!(region.getSoul() instanceof ExtensionRegionSoul)) {
return;
}
final ExtensionElement e = ((ExtensionRegionSoul)region.getSoul()).Element;
if (!(e instanceof BookElement)) {
return;
}
final BookElement element = (BookElement)e;
if (!element.isInitialized()) {
return;
}
final View mainView = (View)BaseActivity.getViewWidget();
final View bookView = BaseActivity.getLayoutInflater().inflate(
ColorProfile.NIGHT.equals(Reader.ViewOptions.ColorProfileName.getValue())
? R.layout.book_popup_night : R.layout.book_popup,
null
);
final int inch = (int)TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_IN, 1, BaseActivity.getResources().getDisplayMetrics()
);
final PopupWindow popup = new PopupWindow(
bookView,
Math.min(4 * inch, mainView.getWidth() * 9 / 10),
Math.min(3 * inch, mainView.getHeight() * 9 / 10)
);
popup.setFocusable(true);
popup.setOutsideTouchable(true);
final ImageView coverView = (ImageView)bookView.findViewById(R.id.book_popup_cover);
if (coverView != null) {
final ZLAndroidImageData imageData = (ZLAndroidImageData)element.getImageData();
if (imageData != null) {
coverView.setImageBitmap(imageData.getFullSizeBitmap());
}
}
final OPDSBookItem item = element.getItem();
final TextView headerView = (TextView)bookView.findViewById(R.id.book_popup_header_text);
final StringBuilder text = new StringBuilder();
for (OPDSBookItem.AuthorData author : item.Authors) {
text.append("<p><i>").append(author.DisplayName).append("</i></p>");
}
text.append("<h3>").append(item.Title).append("</h3>");
headerView.setText(Html.fromHtml(text.toString()));
final TextView descriptionView = (TextView)bookView.findViewById(R.id.book_popup_description_text);
descriptionView.setText(item.getSummary());
descriptionView.setMovementMethod(new LinkMovementMethod());
final ZLResource buttonResource = ZLResource.resource("dialog").getResource("button");
final View buttonsView = bookView.findViewById(R.id.book_popup_buttons);
final Button downloadButton = (Button)buttonsView.findViewById(R.id.ok_button);
downloadButton.setText(buttonResource.getResource("download").getValue());
final List<UrlInfo> infos = item.getAllInfos(UrlInfo.Type.Book);
if (infos.isEmpty() || !(infos.get(0) instanceof BookUrlInfo)) {
downloadButton.setEnabled(false);
} else {
final BookUrlInfo bookInfo = (BookUrlInfo)infos.get(0);
final String fileName = bookInfo.makeBookFileName(UrlInfo.Type.Book);
final Book book = Reader.Collection.getBookByFile(fileName);
if (book != null) {
downloadButton.setText(buttonResource.getResource("openBook").getValue());
downloadButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
openBook(popup, book);
}
});
} else {
final File file = new File(fileName);
if (file.exists()) {
file.delete();
}
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
downloadButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
UIUtil.wait(
"downloadingBook", item.Title.toString(),
new Runnable() {
public void run() {
try {
new QuietNetworkContext().downloadToFile(bookInfo.Url, file);
openBook(popup, Reader.Collection.getBookByFile(fileName));
} catch (ZLNetworkException e) {
UIMessageUtil.showErrorMessage(BaseActivity, "downloadFailed");
e.printStackTrace();
}
}
},
BaseActivity
);
}
});
}
}
final Button cancelButton = (Button)buttonsView.findViewById(R.id.cancel_button);
cancelButton.setText(buttonResource.getResource("cancel").getValue());
cancelButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
popup.dismiss();
}
});
downloadButton.setTextColor(0xFF777777);
cancelButton.setTextColor(0xFF777777);
popup.setOnDismissListener(new PopupWindow.OnDismissListener() {
public void onDismiss() {
}
});
popup.showAtLocation(BaseActivity.getCurrentFocus(), Gravity.CENTER, 0, 0);
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import java.math.BigInteger;
import java.util.Random;
import android.app.AlertDialog;
import android.content.*;
import org.geometerplus.zlibrary.core.options.Config;
import org.geometerplus.zlibrary.core.options.ZLStringOption;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.fbreader.book.Book;
import org.geometerplus.fbreader.book.Bookmark;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.fbreader.formats.ExternalFormatPlugin;
import org.geometerplus.android.fbreader.api.FBReaderIntents;
import org.geometerplus.android.fbreader.formatPlugin.PluginUtil;
import org.geometerplus.android.util.PackageUtil;
class ExternalFileOpener implements FBReaderApp.ExternalFileOpener {
private final String myPluginCode = new BigInteger(80, new Random()).toString();
private final FBReader myReader;
private volatile AlertDialog myDialog;
ExternalFileOpener(FBReader reader) {
myReader = reader;
}
public void openFile(final ExternalFormatPlugin plugin, final Book book, Bookmark bookmark) {
if (myDialog != null) {
myDialog.dismiss();
myDialog = null;
}
final Intent intent = PluginUtil.createIntent(plugin, FBReaderIntents.Action.PLUGIN_VIEW);
FBReaderIntents.putBookExtra(intent, book);
FBReaderIntents.putBookmarkExtra(intent, bookmark);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
new ZLStringOption("PluginCode", plugin.packageName(), "").setValue(myPluginCode);
intent.putExtra("PLUGIN_CODE", myPluginCode);
Config.Instance().runOnConnect(new Runnable() {
public void run() {
try {
myReader.startActivity(intent);
myReader.overridePendingTransition(0, 0);
} catch (ActivityNotFoundException e) {
showErrorDialog(plugin, book);
}
}
});
}
private void showErrorDialog(final ExternalFormatPlugin plugin, final Book book) {
final ZLResource dialogResource = ZLResource.resource("dialog");
final ZLResource buttonResource = dialogResource.getResource("button");
final String title =
dialogResource.getResource("missingPlugin").getResource("title").getValue()
.replace("%s", plugin.supportedFileType());
final AlertDialog.Builder builder = new AlertDialog.Builder(myReader)
.setTitle(title)
.setIcon(0)
.setPositiveButton(buttonResource.getResource("yes").getValue(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
PackageUtil.installFromMarket(myReader, plugin.packageName());
myDialog = null;
}
})
.setNegativeButton(buttonResource.getResource("no").getValue(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
myReader.onPluginNotFound(book);
myDialog = null;
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
myReader.onPluginNotFound(book);
myDialog = null;
}
});
final Runnable showDialog = new Runnable() {
public void run() {
myDialog = builder.create();
myDialog.show();
}
};
if (!myReader.IsPaused) {
myReader.runOnUiThread(showDialog);
} else {
myReader.OnResumeAction = showDialog;
}
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.fbreader.fbreader.FBAction;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
abstract class FBAndroidAction extends FBAction {
protected final FBReader BaseActivity;
FBAndroidAction(FBReader baseActivity, FBReaderApp fbreader) {
super(fbreader);
BaseActivity = baseActivity;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,25 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.zlibrary.ui.android.library.ZLAndroidApplication;
public class FBReaderApplication extends ZLAndroidApplication {
}

View file

@ -0,0 +1,126 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.WindowManager;
import com.github.johnpersano.supertoasts.SuperActivityToast;
import org.geometerplus.zlibrary.core.options.*;
import org.geometerplus.zlibrary.ui.android.library.*;
import org.geometerplus.zlibrary.ui.android.view.AndroidFontUtil;
import org.geometerplus.android.fbreader.dict.DictionaryUtil;
public abstract class FBReaderMainActivity extends Activity {
public static final int REQUEST_PREFERENCES = 1;
public static final int REQUEST_CANCEL_MENU = 2;
public static final int REQUEST_DICTIONARY = 3;
private volatile SuperActivityToast myToast;
@Override
protected void onCreate(Bundle saved) {
super.onCreate(saved);
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler(this));
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
default:
super.onActivityResult(requestCode, resultCode, data);
break;
case REQUEST_DICTIONARY:
DictionaryUtil.onActivityResult(this, resultCode, data);
break;
}
}
public ZLAndroidLibrary getZLibrary() {
return ((ZLAndroidApplication)getApplication()).library();
}
/* ++++++ SCREEN BRIGHTNESS ++++++ */
protected void setScreenBrightnessAuto() {
final WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.screenBrightness = -1.0f;
getWindow().setAttributes(attrs);
}
public void setScreenBrightnessSystem(float level) {
final WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.screenBrightness = level;
getWindow().setAttributes(attrs);
}
public float getScreenBrightnessSystem() {
final float level = getWindow().getAttributes().screenBrightness;
return level >= 0 ? level : .5f;
}
/* ------ SCREEN BRIGHTNESS ------ */
/* ++++++ SUPER TOAST ++++++ */
public boolean isToastShown() {
final SuperActivityToast toast = myToast;
return toast != null && toast.isShowing();
}
public void hideToast() {
final SuperActivityToast toast = myToast;
if (toast != null && toast.isShowing()) {
myToast = null;
runOnUiThread(new Runnable() {
public void run() {
toast.dismiss();
}
});
}
}
public void showToast(final SuperActivityToast toast) {
hideToast();
myToast = toast;
// TODO: avoid this hack (accessing text style via option)
final int dpi = getZLibrary().getDisplayDPI();
final int defaultFontSize = dpi * 18 / 160;
final int fontSize = new ZLIntegerOption("Style", "Base:fontSize", defaultFontSize).getValue();
final int percent = new ZLIntegerRangeOption("Options", "ToastFontSizePercent", 25, 100, 90).getValue();
final int dpFontSize = fontSize * 160 * percent / dpi / 100;
toast.setTextSize(dpFontSize);
toast.setButtonTextSize(dpFontSize * 7 / 8);
final String fontFamily =
new ZLStringOption("Style", "Base:fontFamily", "sans-serif").getValue();
toast.setTypeface(AndroidFontUtil.systemTypeface(fontFamily, false, false));
runOnUiThread(new Runnable() {
public void run() {
toast.show();
}
});
}
/* ------ SUPER TOAST ------ */
public abstract void hideDictionarySelection();
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.text.Html;
import org.geometerplus.zlibrary.core.filesystem.ZLPhysicalFile;
import org.geometerplus.zlibrary.core.filetypes.FileTypeCollection;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.fbreader.book.Book;
import org.geometerplus.fbreader.book.BookUtil;
public abstract class FBUtil {
public static void shareBook(Activity activity, Book book) {
try {
final ZLPhysicalFile file = BookUtil.fileByBook(book).getPhysicalFile();
if (file == null) {
// That should be impossible
return;
}
final CharSequence sharedFrom =
Html.fromHtml(ZLResource.resource("sharing").getResource("sharedFrom").getValue());
activity.startActivity(
new Intent(Intent.ACTION_SEND)
.setType(FileTypeCollection.Instance.rawMimeType(file).Name)
.putExtra(Intent.EXTRA_SUBJECT, book.getTitle())
.putExtra(Intent.EXTRA_TEXT, sharedFrom)
.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file.javaFile()))
);
} catch (ActivityNotFoundException e) {
// TODO: show toast
}
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
public class HideToastAction extends FBAndroidAction {
HideToastAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
public boolean isEnabled() {
return BaseActivity.isToastShown();
}
@Override
protected void run(Object ... params) {
BaseActivity.hideToast();
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.Intent;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
class InstallPluginsAction extends FBAndroidAction {
InstallPluginsAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
BaseActivity.startActivity(new Intent(BaseActivity, PluginListActivity.class));
}
}

View file

@ -0,0 +1,71 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import java.util.*;
import org.geometerplus.zlibrary.core.library.ZLibrary;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.fbreader.fbreader.ActionCode;
import org.geometerplus.android.fbreader.api.MenuNode;
public abstract class MenuData {
private static List<MenuNode> ourNodes;
private static void addToplevelNode(MenuNode node) {
ourNodes.add(node);
}
public static synchronized List<MenuNode> topLevelNodes() {
if (ourNodes == null) {
ourNodes = new ArrayList<MenuNode>();
addToplevelNode(new MenuNode.Item(ActionCode.SHOW_LIBRARY, R.drawable.ic_menu_library));
addToplevelNode(new MenuNode.Item(ActionCode.SHOW_NETWORK_LIBRARY, R.drawable.ic_menu_networklibrary));
addToplevelNode(new MenuNode.Item(ActionCode.SHOW_TOC, R.drawable.ic_menu_toc));
addToplevelNode(new MenuNode.Item(ActionCode.SHOW_BOOKMARKS, R.drawable.ic_menu_bookmarks));
addToplevelNode(new MenuNode.Item(ActionCode.SWITCH_TO_NIGHT_PROFILE, R.drawable.ic_menu_night));
addToplevelNode(new MenuNode.Item(ActionCode.SWITCH_TO_DAY_PROFILE, R.drawable.ic_menu_day));
addToplevelNode(new MenuNode.Item(ActionCode.SEARCH, R.drawable.ic_menu_search));
addToplevelNode(new MenuNode.Item(ActionCode.SHARE_BOOK));
addToplevelNode(new MenuNode.Item(ActionCode.SHOW_PREFERENCES));
addToplevelNode(new MenuNode.Item(ActionCode.SHOW_BOOK_INFO));
final MenuNode.Submenu orientations = new MenuNode.Submenu("screenOrientation");
orientations.Children.add(new MenuNode.Item(ActionCode.SET_SCREEN_ORIENTATION_SYSTEM));
orientations.Children.add(new MenuNode.Item(ActionCode.SET_SCREEN_ORIENTATION_SENSOR));
orientations.Children.add(new MenuNode.Item(ActionCode.SET_SCREEN_ORIENTATION_PORTRAIT));
orientations.Children.add(new MenuNode.Item(ActionCode.SET_SCREEN_ORIENTATION_LANDSCAPE));
if (ZLibrary.Instance().supportsAllOrientations()) {
orientations.Children.add(new MenuNode.Item(ActionCode.SET_SCREEN_ORIENTATION_REVERSE_PORTRAIT));
orientations.Children.add(new MenuNode.Item(ActionCode.SET_SCREEN_ORIENTATION_REVERSE_LANDSCAPE));
}
addToplevelNode(orientations);
addToplevelNode(new MenuNode.Item(ActionCode.INCREASE_FONT));
addToplevelNode(new MenuNode.Item(ActionCode.DECREASE_FONT));
addToplevelNode(new MenuNode.Item(ActionCode.SHOW_NAVIGATION));
addToplevelNode(new MenuNode.Item(ActionCode.INSTALL_PLUGINS));
addToplevelNode(new MenuNode.Item(ActionCode.OPEN_WEB_HELP));
addToplevelNode(new MenuNode.Item(ActionCode.OPEN_START_SCREEN));
ourNodes = Collections.unmodifiableList(ourNodes);
}
return ourNodes;
}
}

View file

@ -0,0 +1,202 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.app.Activity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import org.geometerplus.zlibrary.core.application.ZLApplication;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.text.view.ZLTextView;
import org.geometerplus.zlibrary.text.view.ZLTextWordCursor;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.fbreader.bookmodel.TOCTree;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
final class NavigationPopup extends ZLApplication.PopupPanel {
final static String ID = "NavigationPopup";
private volatile NavigationWindow myWindow;
private volatile FBReader myActivity;
private volatile RelativeLayout myRoot;
private ZLTextWordCursor myStartPosition;
private final FBReaderApp myFBReader;
private volatile boolean myIsInProgress;
NavigationPopup(FBReaderApp fbReader) {
super(fbReader);
myFBReader = fbReader;
}
public void setPanelInfo(FBReader activity, RelativeLayout root) {
myActivity = activity;
myRoot = root;
}
public void runNavigation() {
if (myWindow == null || myWindow.getVisibility() == View.GONE) {
myIsInProgress = false;
if (myStartPosition == null) {
myStartPosition = new ZLTextWordCursor(myFBReader.getTextView().getStartCursor());
}
Application.showPopup(ID);
}
}
@Override
protected void show_() {
if (myActivity != null) {
createPanel(myActivity, myRoot);
}
if (myWindow != null) {
myWindow.show();
setupNavigation();
}
}
@Override
protected void hide_() {
if (myWindow != null) {
myWindow.hide();
}
}
@Override
public String getId() {
return ID;
}
@Override
protected void update() {
if (!myIsInProgress && myWindow != null) {
setupNavigation();
}
}
private void createPanel(FBReader activity, RelativeLayout root) {
if (myWindow != null && activity == myWindow.getContext()) {
return;
}
activity.getLayoutInflater().inflate(R.layout.navigation_panel, root);
myWindow = (NavigationWindow)root.findViewById(R.id.navigation_panel);
final SeekBar slider = (SeekBar)myWindow.findViewById(R.id.navigation_slider);
final TextView text = (TextView)myWindow.findViewById(R.id.navigation_text);
slider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
private void gotoPage(int page) {
final ZLTextView view = myFBReader.getTextView();
if (page == 1) {
view.gotoHome();
} else {
view.gotoPage(page);
}
myFBReader.getViewWidget().reset();
myFBReader.getViewWidget().repaint();
}
public void onStartTrackingTouch(SeekBar seekBar) {
myIsInProgress = true;
}
public void onStopTrackingTouch(SeekBar seekBar) {
myIsInProgress = false;
}
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
final int page = progress + 1;
final int pagesNumber = seekBar.getMax() + 1;
gotoPage(page);
text.setText(makeProgressText(page, pagesNumber));
}
}
});
final Button btnOk = (Button)myWindow.findViewById(R.id.navigation_ok);
final Button btnCancel = (Button)myWindow.findViewById(R.id.navigation_cancel);
View.OnClickListener listener = new View.OnClickListener() {
public void onClick(View v) {
final ZLTextWordCursor position = myStartPosition;
if (v == btnCancel && position != null) {
myFBReader.getTextView().gotoPosition(position);
} else if (v == btnOk) {
if (myStartPosition != null &&
!myStartPosition.equals(myFBReader.getTextView().getStartCursor())) {
myFBReader.addInvisibleBookmark(myStartPosition);
myFBReader.storePosition();
}
}
myStartPosition = null;
Application.hideActivePopup();
myFBReader.getViewWidget().reset();
myFBReader.getViewWidget().repaint();
}
};
btnOk.setOnClickListener(listener);
btnCancel.setOnClickListener(listener);
final ZLResource buttonResource = ZLResource.resource("dialog").getResource("button");
btnOk.setText(buttonResource.getResource("ok").getValue());
btnCancel.setText(buttonResource.getResource("cancel").getValue());
}
private void setupNavigation() {
final SeekBar slider = (SeekBar)myWindow.findViewById(R.id.navigation_slider);
final TextView text = (TextView)myWindow.findViewById(R.id.navigation_text);
final ZLTextView textView = myFBReader.getTextView();
final ZLTextView.PagePosition pagePosition = textView.pagePosition();
if (slider.getMax() != pagePosition.Total - 1 || slider.getProgress() != pagePosition.Current - 1) {
slider.setMax(pagePosition.Total - 1);
slider.setProgress(pagePosition.Current - 1);
text.setText(makeProgressText(pagePosition.Current, pagePosition.Total));
}
}
private String makeProgressText(int page, int pagesNumber) {
final StringBuilder builder = new StringBuilder();
builder.append(page);
builder.append("/");
builder.append(pagesNumber);
final TOCTree tocElement = myFBReader.getCurrentTOCElement();
if (tocElement != null) {
builder.append(" ");
builder.append(tocElement.getText());
}
return builder.toString();
}
final void removeWindow(Activity activity) {
if (myWindow != null && activity == myWindow.getContext()) {
final ViewGroup root = (ViewGroup)myWindow.getParent();
myWindow.hide();
root.removeView(myWindow);
myWindow = null;
}
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.animation.*;
import android.content.Context;
import android.util.AttributeSet;
import android.view.*;
import android.widget.*;
public class NavigationWindow extends LinearLayout {
public NavigationWindow(Context context) {
super(context);
}
public NavigationWindow(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NavigationWindow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private Animator myShowHideAnimator;
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
public void show() {
post(new Runnable() {
public void run() {
setVisibility(View.VISIBLE);
}
});
}
public void hide() {
post(new Runnable() {
public void run() {
setVisibility(View.GONE);
}
});
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.app.NotificationManager;
import android.content.Context;
import org.geometerplus.zlibrary.core.filesystem.ZLPhysicalFile;
import org.geometerplus.fbreader.book.Book;
import org.geometerplus.fbreader.book.BookUtil;
public abstract class NotificationUtil {
public static final int MISSING_BOOK_ID = 0x0fffffff;
private static final int DOWNLOAD_ID_MIN = 0x10000000;
private static final int DOWNLOAD_ID_MAX = 0x1fffffff;
private NotificationUtil() {
}
public static int getDownloadId(String file) {
return DOWNLOAD_ID_MIN + Math.abs(file.hashCode()) % (DOWNLOAD_ID_MAX - DOWNLOAD_ID_MIN + 1);
}
public static void drop(Context context, int id) {
final NotificationManager notificationManager =
(NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(id);
}
public static void drop(Context context, Book book) {
if (book == null) {
return;
}
final ZLPhysicalFile file = BookUtil.fileByBook(book).getPhysicalFile();
if (file == null) {
return;
}
drop(context, getDownloadId(file.getPath()));
}
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import java.util.Map;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import org.geometerplus.zlibrary.core.util.MimeType;
import org.geometerplus.zlibrary.text.view.ZLTextVideoElement;
import org.geometerplus.zlibrary.text.view.ZLTextVideoRegionSoul;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.android.fbreader.httpd.DataUtil;
import org.geometerplus.android.util.UIMessageUtil;
class OpenVideoAction extends FBAndroidAction {
OpenVideoAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
if (params.length != 1 || !(params[0] instanceof ZLTextVideoRegionSoul)) {
return;
}
final ZLTextVideoElement element = ((ZLTextVideoRegionSoul)params[0]).VideoElement;
boolean playerNotFound = false;
for (MimeType mimeType : MimeType.TYPES_VIDEO) {
final String mime = mimeType.toString();
final String path = element.Sources.get(mime);
if (path == null) {
continue;
}
final Intent intent = new Intent(Intent.ACTION_VIEW);
final String url = DataUtil.buildUrl(BaseActivity.DataConnection, mime, path);
if (url == null) {
UIMessageUtil.showErrorMessage(BaseActivity, "videoServiceNotWorking");
return;
}
intent.setDataAndType(Uri.parse(url), mime);
try {
BaseActivity.startActivity(intent);
return;
} catch (ActivityNotFoundException e) {
playerNotFound = true;
continue;
}
}
if (playerNotFound) {
UIMessageUtil.showErrorMessage(BaseActivity, "videoPlayerNotFound");
}
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.Intent;
import android.content.ActivityNotFoundException;
import android.net.Uri;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
class OpenWebHelpAction extends FBAndroidAction {
OpenWebHelpAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
final String url = ZLResource.resource("links").getResource("faqPage").getValue();
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
new Thread(new Runnable() {
public void run() {
BaseActivity.runOnUiThread(new Runnable() {
public void run() {
try {
BaseActivity.startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
}
});
}
}).start();
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.app.Activity;
import android.content.Intent;
public abstract class OrientationUtil {
private static final String KEY = "fbreader.orientation";
public static void startActivity(Activity current, Intent intent) {
current.startActivity(intent.putExtra(KEY, current.getRequestedOrientation()));
}
public static void startActivityForResult(Activity current, Intent intent, int requestCode) {
current.startActivityForResult(intent.putExtra(KEY, current.getRequestedOrientation()), requestCode);
}
public static void setOrientation(Activity activity, Intent intent) {
if (intent == null) {
return;
}
final int orientation = intent.getIntExtra(KEY, Integer.MIN_VALUE);
if (orientation != Integer.MIN_VALUE) {
activity.setRequestedOrientation(orientation);
}
}
private OrientationUtil() {
}
}

View file

@ -0,0 +1,159 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.List;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import android.app.ListActivity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.widget.*;
import android.view.*;
import org.geometerplus.zlibrary.core.filesystem.ZLFile;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.util.XmlUtil;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.android.util.PackageUtil;
import org.geometerplus.android.util.ViewUtil;
public class PluginListActivity extends ListActivity {
private final ZLResource myResource = ZLResource.resource("plugins");
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setTitle(myResource.getValue());
final PluginListAdapter adapter = new PluginListAdapter();
setListAdapter(adapter);
getListView().setOnItemClickListener(adapter);
}
private static class Plugin {
final String Id;
final String PackageName;
Plugin(String id, String packageName) {
Id = id;
PackageName = packageName;
}
}
private class Reader extends DefaultHandler {
final PackageManager myPackageManager = getPackageManager();
final List<Plugin> myPlugins;
Reader(List<Plugin> plugins) {
myPlugins = plugins;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if ("plugin".equals(localName)) {
try {
if (Integer.valueOf(attributes.getValue("min-api")) > Build.VERSION.SDK_INT) {
return;
}
} catch (Throwable t) {
// ignore
}
final String id = attributes.getValue("id");
final String packageName = attributes.getValue("package");
try {
myPackageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
} catch (PackageManager.NameNotFoundException e) {
myPlugins.add(new Plugin(id, packageName));
}
}
}
}
private class PluginListAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
private final List<Plugin> myPlugins = new LinkedList<Plugin>();
PluginListAdapter() {
XmlUtil.parseQuietly(
ZLFile.createFileByPath("default/plugins.xml"),
new Reader(myPlugins)
);
}
public final int getCount() {
return myPlugins.isEmpty() ? 1 : myPlugins.size();
}
public final Plugin getItem(int position) {
return myPlugins.isEmpty() ? null : myPlugins.get(position);
}
public final long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, final ViewGroup parent) {
final View view = convertView != null
? convertView
: LayoutInflater.from(parent.getContext()).inflate(R.layout.plugin_item, parent, false);
final ImageView iconView = (ImageView)view.findViewById(R.id.plugin_item_icon);
final TextView titleView = ViewUtil.findTextView(view, R.id.plugin_item_title);
final TextView summaryView = ViewUtil.findTextView(view, R.id.plugin_item_summary);
final Plugin plugin = getItem(position);
if (plugin != null) {
final ZLResource resource = myResource.getResource(plugin.Id);
titleView.setText(resource.getValue());
summaryView.setText(resource.getResource("summary").getValue());
int iconId = R.drawable.fbreader;
try {
final Field f = R.drawable.class.getField("plugin_" + plugin.Id);
iconId = f.getInt(R.drawable.class);
} catch (Throwable t) {
t.printStackTrace();
}
iconView.setImageResource(iconId);
} else {
final ZLResource resource = myResource.getResource("noMorePlugins");
titleView.setText(resource.getValue());
summaryView.setVisibility(View.GONE);
iconView.setVisibility(View.GONE);
}
return view;
}
public final void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
final Plugin plugin = getItem(position);
if (plugin != null) {
runOnUiThread(new Runnable() {
public void run() {
finish();
PackageUtil.installFromMarket(PluginListActivity.this, plugin.PackageName);
}
});
}
}
}
}

View file

@ -0,0 +1,116 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.app.Activity;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import org.geometerplus.zlibrary.core.application.ZLApplication;
import org.geometerplus.zlibrary.text.view.ZLTextWordCursor;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
abstract class PopupPanel extends ZLApplication.PopupPanel {
public ZLTextWordCursor StartPosition;
protected volatile SimplePopupWindow myWindow;
private volatile FBReader myActivity;
private volatile RelativeLayout myRoot;
PopupPanel(FBReaderApp fbReader) {
super(fbReader);
}
protected final FBReaderApp getReader() {
return (FBReaderApp)Application;
}
@Override
protected void show_() {
if (myActivity != null) {
createControlPanel(myActivity, myRoot);
}
if (myWindow != null) {
myWindow.show();
}
}
@Override
protected void hide_() {
if (myWindow != null) {
myWindow.hide();
}
}
private final void removeWindow(Activity activity) {
if (myWindow != null && activity == myWindow.getContext()) {
final ViewGroup root = (ViewGroup)myWindow.getParent();
myWindow.hide();
root.removeView(myWindow);
myWindow = null;
}
}
public static void removeAllWindows(ZLApplication application, Activity activity) {
for (ZLApplication.PopupPanel popup : application.popupPanels()) {
if (popup instanceof PopupPanel) {
((PopupPanel)popup).removeWindow(activity);
} else if (popup instanceof NavigationPopup) {
((NavigationPopup)popup).removeWindow(activity);
}
}
}
public static void restoreVisibilities(ZLApplication application) {
final ZLApplication.PopupPanel popup = application.getActivePopup();
if (popup instanceof PopupPanel) {
((PopupPanel)popup).show_();
} else if (popup instanceof NavigationPopup) {
((NavigationPopup)popup).show_();
}
}
public final void initPosition() {
if (StartPosition == null) {
StartPosition = new ZLTextWordCursor(getReader().getTextView().getStartCursor());
}
}
public final void storePosition() {
if (StartPosition == null) {
return;
}
final FBReaderApp reader = getReader();
if (!StartPosition.equals(reader.getTextView().getStartCursor())) {
reader.addInvisibleBookmark(StartPosition);
reader.storePosition();
}
}
public void setPanelInfo(FBReader activity, RelativeLayout root) {
myActivity = activity;
myRoot = root;
}
public abstract void createControlPanel(FBReader activity, RelativeLayout root);
}

View file

@ -0,0 +1,206 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.Intent;
import android.content.ActivityNotFoundException;
import android.net.Uri;
import android.os.Parcelable;
import android.view.View;
import com.github.johnpersano.supertoasts.SuperActivityToast;
import com.github.johnpersano.supertoasts.SuperToast;
import com.github.johnpersano.supertoasts.util.OnClickWrapper;
import com.github.johnpersano.supertoasts.util.OnDismissWrapper;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.text.view.*;
import org.geometerplus.fbreader.Paths;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.fbreader.bookmodel.FBHyperlinkType;
import org.geometerplus.fbreader.network.NetworkLibrary;
import org.geometerplus.fbreader.util.AutoTextSnippet;
import org.geometerplus.android.fbreader.dict.DictionaryUtil;
import org.geometerplus.android.fbreader.image.ImageViewActivity;
import org.geometerplus.android.fbreader.network.*;
import org.geometerplus.android.fbreader.network.auth.ActivityNetworkContext;
import org.geometerplus.android.util.UIMessageUtil;
class ProcessHyperlinkAction extends FBAndroidAction {
ProcessHyperlinkAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
public boolean isEnabled() {
return Reader.getTextView().getOutlinedRegion() != null;
}
@Override
protected void run(Object ... params) {
final ZLTextRegion region = Reader.getTextView().getOutlinedRegion();
if (region == null) {
return;
}
final ZLTextRegion.Soul soul = region.getSoul();
if (soul instanceof ZLTextHyperlinkRegionSoul) {
Reader.getTextView().hideOutline();
Reader.getViewWidget().repaint();
final ZLTextHyperlink hyperlink = ((ZLTextHyperlinkRegionSoul)soul).Hyperlink;
switch (hyperlink.Type) {
case FBHyperlinkType.EXTERNAL:
openInBrowser(hyperlink.Id);
break;
case FBHyperlinkType.INTERNAL:
case FBHyperlinkType.FOOTNOTE:
{
final AutoTextSnippet snippet = Reader.getFootnoteData(hyperlink.Id);
if (snippet == null) {
break;
}
Reader.Collection.markHyperlinkAsVisited(Reader.getCurrentBook(), hyperlink.Id);
final boolean showToast;
switch (Reader.MiscOptions.ShowFootnoteToast.getValue()) {
default:
case never:
showToast = false;
break;
case footnotesOnly:
showToast = hyperlink.Type == FBHyperlinkType.FOOTNOTE;
break;
case footnotesAndSuperscripts:
showToast =
hyperlink.Type == FBHyperlinkType.FOOTNOTE ||
region.isVerticallyAligned();
break;
case allInternalLinks:
showToast = true;
break;
}
if (showToast) {
final SuperActivityToast toast;
if (snippet.IsEndOfText) {
toast = new SuperActivityToast(BaseActivity, SuperToast.Type.STANDARD);
} else {
toast = new SuperActivityToast(BaseActivity, SuperToast.Type.BUTTON);
toast.setButtonIcon(
android.R.drawable.ic_menu_more,
ZLResource.resource("toast").getResource("more").getValue()
);
toast.setOnClickWrapper(new OnClickWrapper("ftnt", new SuperToast.OnClickListener() {
@Override
public void onClick(View view, Parcelable token) {
Reader.getTextView().hideOutline();
Reader.tryOpenFootnote(hyperlink.Id);
}
}));
}
toast.setText(snippet.getText());
toast.setDuration(Reader.MiscOptions.FootnoteToastDuration.getValue().Value);
toast.setOnDismissWrapper(new OnDismissWrapper("ftnt", new SuperToast.OnDismissListener() {
@Override
public void onDismiss(View view) {
Reader.getTextView().hideOutline();
Reader.getViewWidget().repaint();
}
}));
Reader.getTextView().outlineRegion(region);
BaseActivity.showToast(toast);
} else {
Reader.tryOpenFootnote(hyperlink.Id);
}
break;
}
}
} else if (soul instanceof ZLTextImageRegionSoul) {
Reader.getTextView().hideOutline();
Reader.getViewWidget().repaint();
final String url = ((ZLTextImageRegionSoul)soul).ImageElement.URL;
if (url != null) {
try {
final Intent intent = new Intent();
intent.setClass(BaseActivity, ImageViewActivity.class);
intent.putExtra(ImageViewActivity.URL_KEY, url);
intent.putExtra(
ImageViewActivity.BACKGROUND_COLOR_KEY,
Reader.ImageOptions.ImageViewBackground.getValue().intValue()
);
OrientationUtil.startActivity(BaseActivity, intent);
} catch (Exception e) {
e.printStackTrace();
}
}
} else if (soul instanceof ZLTextWordRegionSoul) {
DictionaryUtil.openTextInDictionary(
BaseActivity,
((ZLTextWordRegionSoul)soul).Word.getString(),
true,
region.getTop(),
region.getBottom(),
new Runnable() {
public void run() {
BaseActivity.outlineRegion(soul);
}
}
);
}
}
private void openInBrowser(final String url) {
final Intent intent = new Intent(Intent.ACTION_VIEW);
final boolean externalUrl;
if (BookDownloader.acceptsUri(Uri.parse(url), null)) {
intent.setClass(BaseActivity, BookDownloader.class);
intent.putExtra(BookDownloaderService.Key.SHOW_NOTIFICATIONS, BookDownloaderService.Notifications.ALL);
externalUrl = false;
} else {
externalUrl = true;
}
final NetworkLibrary nLibrary = NetworkLibrary.Instance(Paths.systemInfo(BaseActivity));
new Thread(new Runnable() {
public void run() {
if (!url.startsWith("fbreader-action:")) {
try {
nLibrary.initialize(new ActivityNetworkContext(BaseActivity));
} catch (ZLNetworkException e) {
e.printStackTrace();
UIMessageUtil.showMessageText(BaseActivity, e.getMessage());
return;
}
}
intent.setData(Util.rewriteUri(Uri.parse(nLibrary.rewriteUrl(url, externalUrl))));
BaseActivity.runOnUiThread(new Runnable() {
public void run() {
try {
OrientationUtil.startActivity(BaseActivity, intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
}
});
}
}).start();
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.Intent;
import android.content.ActivityNotFoundException;
import android.net.Uri;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.android.fbreader.api.PluginApi;
class RunPluginAction extends FBAndroidAction {
private final Uri myUri;
RunPluginAction(FBReader baseActivity, FBReaderApp fbreader, Uri uri) {
super(baseActivity, fbreader);
myUri = uri;
}
@Override
protected void run(Object ... params) {
if (myUri == null) {
return;
}
try {
OrientationUtil.startActivity(
BaseActivity, new Intent(PluginApi.ACTION_RUN, myUri)
);
} catch (ActivityNotFoundException e) {
}
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
class SearchAction extends FBAndroidAction {
SearchAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
public boolean isVisible() {
return Reader.Model != null;
}
@Override
protected void run(Object ... params) {
BaseActivity.onSearchRequested();
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.Intent;
import android.os.Parcelable;
import android.view.View;
import com.github.johnpersano.supertoasts.SuperActivityToast;
import com.github.johnpersano.supertoasts.SuperToast;
import com.github.johnpersano.supertoasts.util.OnClickWrapper;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.fbreader.book.Bookmark;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.android.fbreader.api.FBReaderIntents;
import org.geometerplus.android.fbreader.bookmark.EditBookmarkActivity;
public class SelectionBookmarkAction extends FBAndroidAction {
SelectionBookmarkAction(FBReader baseApplication, FBReaderApp fbreader) {
super(baseApplication, fbreader);
}
@Override
protected void run(Object ... params) {
final Bookmark bookmark;
if (params.length != 0) {
bookmark = (Bookmark)params[0];
} else {
bookmark = Reader.addSelectionBookmark();
}
if (bookmark == null) {
return;
}
final SuperActivityToast toast =
new SuperActivityToast(BaseActivity, SuperToast.Type.BUTTON);
toast.setText(bookmark.getText());
toast.setDuration(SuperToast.Duration.EXTRA_LONG);
toast.setButtonIcon(
android.R.drawable.ic_menu_edit,
ZLResource.resource("dialog").getResource("button").getResource("edit").getValue()
);
toast.setOnClickWrapper(new OnClickWrapper("bkmk", new SuperToast.OnClickListener() {
@Override
public void onClick(View view, Parcelable token) {
final Intent intent =
new Intent(BaseActivity.getApplicationContext(), EditBookmarkActivity.class);
FBReaderIntents.putBookmarkExtra(intent, bookmark);
OrientationUtil.startActivity(BaseActivity, intent);
}
}));
BaseActivity.showToast(toast);
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.app.Application;
import android.text.ClipboardManager;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.fbreader.fbreader.FBView;
import org.geometerplus.fbreader.util.TextSnippet;
import org.geometerplus.android.util.UIMessageUtil;
public class SelectionCopyAction extends FBAndroidAction {
SelectionCopyAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
final FBView fbview = Reader.getTextView();
final TextSnippet snippet = fbview.getSelectedSnippet();
if (snippet == null) {
return;
}
final String text = snippet.getText();
fbview.clearSelection();
final ClipboardManager clipboard =
(ClipboardManager)BaseActivity.getApplication().getSystemService(Application.CLIPBOARD_SERVICE);
clipboard.setText(text);
UIMessageUtil.showMessageText(
BaseActivity,
ZLResource.resource("selection").getResource("textInBuffer").getValue().replace("%s", clipboard.getText())
);
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
class SelectionHidePanelAction extends FBAndroidAction {
SelectionHidePanelAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
BaseActivity.hideSelectionPanel();
}
}

View file

@ -0,0 +1,112 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.view.View;
import android.widget.RelativeLayout;
import org.geometerplus.fbreader.fbreader.ActionCode;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.zlibrary.ui.android.R;
class SelectionPopup extends PopupPanel implements View.OnClickListener {
final static String ID = "SelectionPopup";
SelectionPopup(FBReaderApp fbReader) {
super(fbReader);
}
@Override
public String getId() {
return ID;
}
@Override
public void createControlPanel(FBReader activity, RelativeLayout root) {
if (myWindow != null && activity == myWindow.getContext()) {
return;
}
activity.getLayoutInflater().inflate(R.layout.selection_panel, root);
myWindow = (SimplePopupWindow)root.findViewById(R.id.selection_panel);
setupButton(R.id.selection_panel_copy);
setupButton(R.id.selection_panel_share);
setupButton(R.id.selection_panel_translate);
setupButton(R.id.selection_panel_bookmark);
setupButton(R.id.selection_panel_close);
}
private void setupButton(int buttonId) {
myWindow.findViewById(buttonId).setOnClickListener(this);
}
public void move(int selectionStartY, int selectionEndY) {
if (myWindow == null) {
return;
}
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT
);
layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
final int verticalPosition;
final int screenHeight = ((View)myWindow.getParent()).getHeight();
final int diffTop = screenHeight - selectionEndY;
final int diffBottom = selectionStartY;
if (diffTop > diffBottom) {
verticalPosition = diffTop > myWindow.getHeight() + 20
? RelativeLayout.ALIGN_PARENT_BOTTOM : RelativeLayout.CENTER_VERTICAL;
} else {
verticalPosition = diffBottom > myWindow.getHeight() + 20
? RelativeLayout.ALIGN_PARENT_TOP : RelativeLayout.CENTER_VERTICAL;
}
layoutParams.addRule(verticalPosition);
myWindow.setLayoutParams(layoutParams);
}
@Override
protected void update() {
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.selection_panel_copy:
Application.runAction(ActionCode.SELECTION_COPY_TO_CLIPBOARD);
break;
case R.id.selection_panel_share:
Application.runAction(ActionCode.SELECTION_SHARE);
break;
case R.id.selection_panel_translate:
Application.runAction(ActionCode.SELECTION_TRANSLATE);
break;
case R.id.selection_panel_bookmark:
Application.runAction(ActionCode.SELECTION_BOOKMARK);
break;
case R.id.selection_panel_close:
Application.runAction(ActionCode.SELECTION_CLEAR);
break;
}
Application.hideActivePopup();
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.Intent;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.fbreader.fbreader.FBView;
import org.geometerplus.fbreader.util.TextSnippet;
public class SelectionShareAction extends FBAndroidAction {
SelectionShareAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
final FBView fbview = Reader.getTextView();
final TextSnippet snippet = fbview.getSelectedSnippet();
if (snippet == null) {
return;
}
final String text = snippet.getText();
final String title = Reader.getCurrentBook().getTitle();
fbview.clearSelection();
final Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT,
ZLResource.resource("selection").getResource("quoteFrom").getValue().replace("%s", title)
);
intent.putExtra(android.content.Intent.EXTRA_TEXT, text);
BaseActivity.startActivity(Intent.createChooser(intent, null));
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
class SelectionShowPanelAction extends FBAndroidAction {
SelectionShowPanelAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
public boolean isEnabled() {
return !Reader.getTextView().isSelectionEmpty();
}
@Override
protected void run(Object ... params) {
BaseActivity.showSelectionPanel();
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.fbreader.fbreader.*;
import org.geometerplus.fbreader.util.TextSnippet;
import org.geometerplus.android.fbreader.dict.DictionaryUtil;
public class SelectionTranslateAction extends FBAndroidAction {
SelectionTranslateAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
final FBView fbview = Reader.getTextView();
final DictionaryHighlighting dictionaryHilite = DictionaryHighlighting.get(fbview);
final TextSnippet snippet = fbview.getSelectedSnippet();
if (dictionaryHilite == null || snippet == null) {
return;
}
DictionaryUtil.openTextInDictionary(
BaseActivity,
snippet.getText(),
fbview.getCountOfSelectedWords() == 1,
fbview.getSelectionStartY(),
fbview.getSelectionEndY(),
new Runnable() {
public void run() {
fbview.addHighlighting(dictionaryHilite);
Reader.getViewWidget().repaint();
}
}
);
fbview.clearSelection();
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import org.geometerplus.zlibrary.core.library.ZLibrary;
import org.geometerplus.zlibrary.core.util.ZLBoolean3;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
class SetScreenOrientationAction extends FBAndroidAction {
static void setOrientation(Activity activity, String optionValue) {
int orientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
if (ZLibrary.SCREEN_ORIENTATION_SENSOR.equals(optionValue)) {
orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
} else if (ZLibrary.SCREEN_ORIENTATION_PORTRAIT.equals(optionValue)) {
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
} else if (ZLibrary.SCREEN_ORIENTATION_LANDSCAPE.equals(optionValue)) {
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
} else if (ZLibrary.SCREEN_ORIENTATION_REVERSE_PORTRAIT.equals(optionValue)) {
orientation = 9; // ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
} else if (ZLibrary.SCREEN_ORIENTATION_REVERSE_LANDSCAPE.equals(optionValue)) {
orientation = 8; // ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
}
activity.setRequestedOrientation(orientation);
}
private final String myOptionValue;
SetScreenOrientationAction(FBReader baseActivity, FBReaderApp fbreader, String optionValue) {
super(baseActivity, fbreader);
myOptionValue = optionValue;
}
@Override
public ZLBoolean3 isChecked() {
return myOptionValue.equals(ZLibrary.Instance().getOrientationOption().getValue())
? ZLBoolean3.B3_TRUE : ZLBoolean3.B3_FALSE;
}
@Override
protected void run(Object ... params) {
setOrientation(BaseActivity, myOptionValue);
ZLibrary.Instance().getOrientationOption().setValue(myOptionValue);
Reader.onRepaintFinished();
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (C) 2012-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.fbreader.book.Book;
import org.geometerplus.fbreader.book.BookUtil;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
public class ShareBookAction extends FBAndroidAction {
ShareBookAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
public boolean isVisible() {
final Book book = Reader.getCurrentBook();
return book != null && BookUtil.fileByBook(book).getPhysicalFile() != null;
}
@Override
protected void run(Object ... params) {
FBUtil.shareBook(BaseActivity, Reader.getCurrentBook());
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.Intent;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.android.fbreader.api.FBReaderIntents;
import org.geometerplus.android.fbreader.library.BookInfoActivity;
class ShowBookInfoAction extends FBAndroidAction {
ShowBookInfoAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
public boolean isVisible() {
return Reader.Model != null;
}
@Override
protected void run(Object ... params) {
final Intent intent =
new Intent(BaseActivity.getApplicationContext(), BookInfoActivity.class)
.putExtra(BookInfoActivity.FROM_READING_MODE_KEY, true);
FBReaderIntents.putBookExtra(intent, Reader.getCurrentBook());
OrientationUtil.startActivity(BaseActivity, intent);
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.android.fbreader.api.FBReaderIntents;
import org.geometerplus.android.fbreader.bookmark.BookmarksActivity;
import org.geometerplus.android.util.PackageUtil;
class ShowBookmarksAction extends FBAndroidAction {
ShowBookmarksAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
public boolean isVisible() {
return Reader.Model != null;
}
@Override
protected void run(Object ... params) {
final Intent externalIntent =
new Intent(FBReaderIntents.Action.EXTERNAL_BOOKMARKS);
final Intent internalIntent =
new Intent(BaseActivity.getApplicationContext(), BookmarksActivity.class);
if (PackageUtil.canBeStarted(BaseActivity, externalIntent, true)) {
try {
startBookmarksActivity(externalIntent);
} catch (ActivityNotFoundException e) {
startBookmarksActivity(internalIntent);
}
} else {
startBookmarksActivity(internalIntent);
}
}
private void startBookmarksActivity(Intent intent) {
FBReaderIntents.putBookExtra(intent, Reader.getCurrentBook());
FBReaderIntents.putBookmarkExtra(intent, Reader.createBookmark(80, true));
OrientationUtil.startActivity(BaseActivity, intent);
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.android.fbreader.api.FBReaderIntents;
class ShowCancelMenuAction extends FBAndroidAction {
ShowCancelMenuAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
if (!Reader.jumpBack()) {
if (Reader.hasCancelActions()) {
BaseActivity.startActivityForResult(
FBReaderIntents.defaultInternalIntent(FBReaderIntents.Action.CANCEL_MENU),
FBReader.REQUEST_CANCEL_MENU
);
} else {
Reader.closeWindow();
}
}
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.android.fbreader.api.FBReaderIntents;
import org.geometerplus.android.fbreader.library.LibraryActivity;
import org.geometerplus.android.util.PackageUtil;
class ShowLibraryAction extends FBAndroidAction {
ShowLibraryAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
final Intent externalIntent =
new Intent(FBReaderIntents.Action.EXTERNAL_LIBRARY);
final Intent internalIntent =
new Intent(BaseActivity.getApplicationContext(), LibraryActivity.class);
if (PackageUtil.canBeStarted(BaseActivity, externalIntent, true)) {
try {
startLibraryActivity(externalIntent);
} catch (ActivityNotFoundException e) {
startLibraryActivity(internalIntent);
}
} else {
startLibraryActivity(internalIntent);
}
}
private void startLibraryActivity(Intent intent) {
if (Reader.Model != null) {
FBReaderIntents.putBookExtra(intent, Reader.getCurrentBook());
}
OrientationUtil.startActivity(BaseActivity, intent);
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
class ShowMenuAction extends FBAndroidAction {
ShowMenuAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
BaseActivity.openOptionsMenu();
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.zlibrary.text.model.ZLTextModel;
import org.geometerplus.zlibrary.text.view.ZLTextView;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
class ShowNavigationAction extends FBAndroidAction {
ShowNavigationAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
public boolean isVisible() {
final ZLTextView view = (ZLTextView)Reader.getCurrentView();
final ZLTextModel textModel = view.getModel();
return textModel != null && textModel.getParagraphsNumber() != 0;
}
@Override
protected void run(Object ... params) {
BaseActivity.navigate();
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.Intent;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.android.fbreader.network.NetworkLibraryPrimaryActivity;
class ShowNetworkLibraryAction extends FBAndroidAction {
ShowNetworkLibraryAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
OrientationUtil.startActivity(
BaseActivity, new Intent(
BaseActivity.getApplicationContext(),
NetworkLibraryPrimaryActivity.class
)
);
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.Intent;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.android.fbreader.preferences.PreferenceActivity;
class ShowPreferencesAction extends FBAndroidAction {
ShowPreferencesAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
final Intent intent =
new Intent(BaseActivity.getApplicationContext(), PreferenceActivity.class);
if (params.length == 1 && params[0] instanceof String) {
intent.putExtra(PreferenceActivity.SCREEN_KEY, (String)params[0]);
}
OrientationUtil.startActivityForResult(BaseActivity, intent, FBReader.REQUEST_PREFERENCES);
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.Intent;
import org.geometerplus.fbreader.bookmodel.BookModel;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
class ShowTOCAction extends FBAndroidAction {
ShowTOCAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
static boolean isTOCAvailable(FBReaderApp reader) {
if (reader == null) {
return false;
}
final BookModel model = reader.Model;
return model != null && model.TOCTree.hasChildren();
}
@Override
public boolean isVisible() {
return isTOCAvailable(Reader);
}
@Override
protected void run(Object ... params) {
OrientationUtil.startActivity(
BaseActivity, new Intent(BaseActivity.getApplicationContext(), TOCActivity.class)
);
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
public class SimplePopupWindow extends FrameLayout {
public SimplePopupWindow(Context context) {
super(context);
}
public SimplePopupWindow(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SimplePopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
public void show() {
post(new Runnable() {
public void run() {
setVisibility(View.VISIBLE);
}
});
}
public void hide() {
post(new Runnable() {
public void run() {
setVisibility(View.GONE);
}
});
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
class StartScreenAction extends FBAndroidAction {
StartScreenAction(FBReader baseActivity, FBReaderApp fbreader) {
super(baseActivity, fbreader);
}
@Override
protected void run(Object ... params) {
Reader.openHelpBook();
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
class SwitchProfileAction extends FBAndroidAction {
private String myProfileName;
SwitchProfileAction(FBReader baseActivity, FBReaderApp fbreader, String profileName) {
super(baseActivity, fbreader);
myProfileName = profileName;
}
@Override
public boolean isVisible() {
return !myProfileName.equals(Reader.ViewOptions.ColorProfileName.getValue());
}
@Override
protected void run(Object ... params) {
Reader.ViewOptions.ColorProfileName.setValue(myProfileName);
Reader.getViewWidget().reset();
Reader.getViewWidget().repaint();
}
}

View file

@ -0,0 +1,138 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.*;
import android.widget.*;
import org.geometerplus.zlibrary.core.application.ZLApplication;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.tree.ZLTree;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.zlibrary.text.view.ZLTextWordCursor;
import org.geometerplus.fbreader.bookmodel.TOCTree;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.android.util.ViewUtil;
public class TOCActivity extends ListActivity {
private TOCAdapter myAdapter;
private ZLTree<?> mySelectedItem;
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
Thread.setDefaultUncaughtExceptionHandler(new org.geometerplus.zlibrary.ui.android.library.UncaughtExceptionHandler(this));
requestWindowFeature(Window.FEATURE_NO_TITLE);
final FBReaderApp fbreader = (FBReaderApp)ZLApplication.Instance();
final TOCTree root = fbreader.Model.TOCTree;
myAdapter = new TOCAdapter(root);
TOCTree treeToSelect = fbreader.getCurrentTOCElement();
myAdapter.selectItem(treeToSelect);
mySelectedItem = treeToSelect;
}
@Override
protected void onStart() {
super.onStart();
OrientationUtil.setOrientation(this, getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
OrientationUtil.setOrientation(this, intent);
}
private static final int PROCESS_TREE_ITEM_ID = 0;
private static final int READ_BOOK_ITEM_ID = 1;
@Override
public boolean onContextItemSelected(MenuItem item) {
final int position = ((AdapterView.AdapterContextMenuInfo)item.getMenuInfo()).position;
final TOCTree tree = (TOCTree)myAdapter.getItem(position);
switch (item.getItemId()) {
case PROCESS_TREE_ITEM_ID:
myAdapter.runTreeItem(tree);
return true;
case READ_BOOK_ITEM_ID:
myAdapter.openBookText(tree);
return true;
}
return super.onContextItemSelected(item);
}
private final class TOCAdapter extends ZLTreeAdapter {
TOCAdapter(TOCTree root) {
super(getListView(), root);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
final int position = ((AdapterView.AdapterContextMenuInfo)menuInfo).position;
final TOCTree tree = (TOCTree)getItem(position);
if (tree.hasChildren()) {
menu.setHeaderTitle(tree.getText());
final ZLResource resource = ZLResource.resource("tocView");
menu.add(0, PROCESS_TREE_ITEM_ID, 0, resource.getResource(isOpen(tree) ? "collapseTree" : "expandTree").getValue());
menu.add(0, READ_BOOK_ITEM_ID, 0, resource.getResource("readText").getValue());
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final View view = (convertView != null) ? convertView :
LayoutInflater.from(parent.getContext()).inflate(R.layout.toc_tree_item, parent, false);
final TOCTree tree = (TOCTree)getItem(position);
view.setBackgroundColor(tree == mySelectedItem ? 0xff808080 : 0);
setIcon(ViewUtil.findImageView(view, R.id.toc_tree_item_icon), tree);
ViewUtil.findTextView(view, R.id.toc_tree_item_text).setText(tree.getText());
return view;
}
void openBookText(TOCTree tree) {
final TOCTree.Reference reference = tree.getReference();
if (reference != null) {
finish();
final FBReaderApp fbreader = (FBReaderApp)ZLApplication.Instance();
fbreader.addInvisibleBookmark();
fbreader.BookTextView.gotoPosition(reference.ParagraphIndex, 0, 0);
fbreader.showBookTextView();
fbreader.storePosition();
}
}
@Override
protected boolean runTreeItem(ZLTree<?> tree) {
if (super.runTreeItem(tree)) {
return true;
}
openBookText((TOCTree)tree);
return true;
}
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import android.view.View;
import android.widget.RelativeLayout;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.fbreader.fbreader.ActionCode;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
final class TextSearchPopup extends PopupPanel implements View.OnClickListener {
final static String ID = "TextSearchPopup";
TextSearchPopup(FBReaderApp fbReader) {
super(fbReader);
}
@Override
public String getId() {
return ID;
}
@Override
protected void hide_() {
getReader().getTextView().clearFindResults();
super.hide_();
}
@Override
public synchronized void createControlPanel(FBReader activity, RelativeLayout root) {
if (myWindow != null && activity == myWindow.getContext()) {
return;
}
activity.getLayoutInflater().inflate(R.layout.search_panel, root);
myWindow = (SimplePopupWindow)root.findViewById(R.id.search_panel);
setupButton(R.id.search_panel_previous);
setupButton(R.id.search_panel_next);
setupButton(R.id.search_panel_close);
}
private void setupButton(int buttonId) {
myWindow.findViewById(buttonId).setOnClickListener(this);
}
@Override
protected synchronized void update() {
if (myWindow == null) {
return;
}
myWindow.findViewById(R.id.search_panel_previous).setEnabled(
Application.isActionEnabled(ActionCode.FIND_PREVIOUS)
);
myWindow.findViewById(R.id.search_panel_next).setEnabled(
Application.isActionEnabled(ActionCode.FIND_NEXT)
);
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.search_panel_previous:
Application.runAction(ActionCode.FIND_PREVIOUS);
break;
case R.id.search_panel_next:
Application.runAction(ActionCode.FIND_NEXT);
break;
case R.id.search_panel_close:
Application.runAction(ActionCode.CLEAR_FIND_RESULTS);
storePosition();
StartPosition = null;
Application.hideActivePopup();
}
}
}

View file

@ -0,0 +1,185 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
import java.util.HashSet;
import android.view.*;
import android.widget.*;
import org.geometerplus.zlibrary.core.tree.ZLTree;
import org.geometerplus.zlibrary.ui.android.R;
public abstract class ZLTreeAdapter extends BaseAdapter implements AdapterView.OnItemClickListener, View.OnCreateContextMenuListener {
private final ListView myParent;
private final ZLTree<?> Root;
private ZLTree<?>[] myItems;
private final HashSet<ZLTree<?>> myOpenItems = new HashSet<ZLTree<?>>();
protected ZLTreeAdapter(ListView parent, ZLTree<?> root) {
myParent = parent;
Root = root;
myItems = new ZLTree[root.getSize() - 1];
myOpenItems.add(root);
parent.setAdapter(this);
parent.setOnItemClickListener(this);
parent.setOnCreateContextMenuListener(this);
}
protected final void openTree(ZLTree<?> tree) {
if (tree == null) {
return;
}
while (!myOpenItems.contains(tree)) {
myOpenItems.add(tree);
tree = tree.Parent;
}
}
public final void expandOrCollapseTree(ZLTree<?> tree) {
if (!tree.hasChildren()) {
return;
}
if (isOpen(tree)) {
myOpenItems.remove(tree);
} else {
myOpenItems.add(tree);
}
//myParent.invalidateViews();
//myParent.requestLayout();
notifyDataSetChanged();
}
public final boolean isOpen(ZLTree<?> tree) {
return myOpenItems.contains(tree);
}
public final void selectItem(ZLTree<?> tree) {
if (tree == null) {
return;
}
openTree(tree.Parent);
int index = 0;
while (true) {
ZLTree<?> parent = tree.Parent;
if (parent == null) {
break;
}
for (ZLTree<?> sibling : parent.subtrees()) {
if (sibling == tree) {
break;
}
index += getCount(sibling);
}
tree = parent;
++index;
}
if (index > 0) {
myParent.setSelection(index - 1);
}
myParent.invalidateViews();
}
private int getCount(ZLTree<?> tree) {
int count = 1;
if (isOpen(tree)) {
for (ZLTree<?> subtree : tree.subtrees()) {
count += getCount(subtree);
}
}
return count;
}
public final int getCount() {
return getCount(Root) - 1;
}
private final int indexByPosition(int position, ZLTree<?> tree) {
if (position == 0) {
return 0;
}
--position;
int index = 1;
for (ZLTree<?> subtree : tree.subtrees()) {
int count = getCount(subtree);
if (count <= position) {
position -= count;
index += subtree.getSize();
} else {
return index + indexByPosition(position, subtree);
}
}
throw new RuntimeException("That's impossible!!!");
}
public final ZLTree<?> getItem(int position) {
final int index = indexByPosition(position + 1, Root) - 1;
ZLTree<?> item = myItems[index];
if (item == null) {
item = Root.getTreeByParagraphNumber(index + 1);
myItems[index] = item;
}
return item;
}
public final boolean areAllItemsEnabled() {
return true;
}
public final boolean isEnabled(int position) {
return true;
}
public final long getItemId(int position) {
return indexByPosition(position + 1, Root);
}
protected boolean runTreeItem(ZLTree<?> tree) {
if (!tree.hasChildren()) {
return false;
}
expandOrCollapseTree(tree);
return true;
}
public final void onItemClick(AdapterView<?> parent, View view, int position, long id) {
runTreeItem(getItem(position));
}
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
}
public abstract View getView(int position, View convertView, ViewGroup parent);
protected final void setIcon(ImageView imageView, ZLTree<?> tree) {
if (tree.hasChildren()) {
if (isOpen(tree)) {
imageView.setImageResource(R.drawable.ic_list_group_open);
} else {
imageView.setImageResource(R.drawable.ic_list_group_closed);
}
} else {
imageView.setImageResource(R.drawable.ic_list_group_empty);
}
imageView.setPadding(25 * (tree.Level - 1), imageView.getPaddingTop(), 0, imageView.getPaddingBottom());
}
}

View file

@ -0,0 +1,95 @@
/*
* This code is in the public domain.
*/
package org.geometerplus.android.fbreader.api;
import java.util.List;
import java.util.Date;
import android.graphics.Bitmap;
public interface Api {
// information about fbreader
String getFBReaderVersion() throws ApiException;
// preferences information
List<String> getOptionGroups() throws ApiException;
List<String> getOptionNames(String group) throws ApiException;
String getOptionValue(String group, String name) throws ApiException;
void setOptionValue(String group, String name, String value) throws ApiException;
// book information for current book
String getBookLanguage() throws ApiException;
String getBookTitle() throws ApiException;
List<String> getBookAuthors() throws ApiException;
List<String> getBookTags() throws ApiException;
String getBookFilePath() throws ApiException;
String getBookHash() throws ApiException;
String getBookUniqueId() throws ApiException;
Date getBookLastTurningTime() throws ApiException;
// book information for book defined by id
String getBookLanguage(long id) throws ApiException;
String getBookTitle(long id) throws ApiException;
//List<String> getBookAuthors(long id) throws ApiException;
List<String> getBookTags(long id) throws ApiException;
String getBookFilePath(long id) throws ApiException;
String getBookHash(long id) throws ApiException;
String getBookUniqueId(long id) throws ApiException;
Date getBookLastTurningTime(long id) throws ApiException;
float getBookProgress() throws ApiException;
//long findBookIdByUniqueId(String uniqueId) throws ApiException;
//long findBookIdByFilePath(String uniqueId) throws ApiException;
// text information
int getParagraphsNumber() throws ApiException;
int getParagraphElementsCount(int paragraphIndex) throws ApiException;
String getParagraphText(int paragraphIndex) throws ApiException;
List<String> getParagraphWords(int paragraphIndex) throws ApiException;
List<Integer> getParagraphWordIndices(int paragraphIndex) throws ApiException;
// page information
TextPosition getPageStart() throws ApiException;
TextPosition getPageEnd() throws ApiException;
boolean isPageEndOfSection() throws ApiException;
boolean isPageEndOfText() throws ApiException;
// manage view
void setPageStart(TextPosition position) throws ApiException;
void highlightArea(TextPosition start, TextPosition end) throws ApiException;
void clearHighlighting() throws ApiException;
int getBottomMargin() throws ApiException;
void setBottomMargin(int value) throws ApiException;
int getTopMargin() throws ApiException;
void setTopMargin(int value) throws ApiException;
int getLeftMargin() throws ApiException;
void setLeftMargin(int value) throws ApiException;
int getRightMargin() throws ApiException;
void setRightMargin(int value) throws ApiException;
// action control
List<String> listActions() throws ApiException;
List<String> listActionNames(List<String> actions) throws ApiException;
String getKeyAction(int key, boolean longPress) throws ApiException;
void setKeyAction(int key, boolean longPress, String action) throws ApiException;
List<String> listZoneMaps() throws ApiException;
String getZoneMap() throws ApiException;
void setZoneMap(String name) throws ApiException;
int getZoneMapHeight(String name) throws ApiException;
int getZoneMapWidth(String name) throws ApiException;
void createZoneMap(String name, int width, int height) throws ApiException;
boolean isZoneMapCustom(String name) throws ApiException;
void deleteZoneMap(String name) throws ApiException;
String getTapZoneAction(String name, int h, int v, boolean singleTap) throws ApiException;
void setTapZoneAction(String name, int h, int v, boolean singleTap, String action) throws ApiException;
String getTapActionByCoordinates(String name, int x, int y, int width, int height, String tap) throws ApiException;
List<MenuNode> getMainMenuContent() throws ApiException;
String getResourceString(String ... keys) throws ApiException;
Bitmap getBitmap(int resourceId) throws ApiException;
}

View file

@ -0,0 +1,528 @@
/*
* This code is in the public domain.
*/
package org.geometerplus.android.fbreader.api;
import java.io.Serializable;
import java.util.*;
import android.content.*;
import android.graphics.Bitmap;
import android.os.IBinder;
import android.os.Parcelable;
public class ApiClientImplementation implements ServiceConnection, Api, ApiMethods {
public static interface ConnectionListener {
void onConnected();
}
static final String EVENT_TYPE = "event.type";
private final Context myContext;
private ConnectionListener myListener;
private volatile ApiInterface myInterface;
private final List<ApiListener> myApiListeners =
Collections.synchronizedList(new LinkedList<ApiListener>());
private final BroadcastReceiver myEventReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (myInterface == null || myApiListeners.size() == 0) {
return;
}
final int code = intent.getIntExtra(EVENT_TYPE, -1);
if (code != -1) {
synchronized (myApiListeners) {
for (ApiListener l : myApiListeners) {
l.onEvent(code);
}
}
}
}
};
public ApiClientImplementation(Context context, ConnectionListener listener) {
myContext = context;
myListener = listener;
connect();
}
public synchronized void connect() {
if (myInterface == null) {
myContext.bindService(FBReaderIntents.defaultInternalIntent(FBReaderIntents.Action.API), this, Context.BIND_AUTO_CREATE);
myContext.registerReceiver(myEventReceiver, new IntentFilter(FBReaderIntents.Action.API_CALLBACK));
}
}
public synchronized void disconnect() {
if (myInterface != null) {
myContext.unregisterReceiver(myEventReceiver);
try {
myContext.unbindService(this);
} catch (IllegalArgumentException e) {
}
myInterface = null;
}
}
public void addListener(ApiListener listener) {
myApiListeners.add(listener);
}
public void removeListener(ApiListener listener) {
myApiListeners.remove(listener);
}
public synchronized void onServiceConnected(ComponentName className, IBinder service) {
myInterface = ApiInterface.Stub.asInterface(service);
if (myListener != null) {
myListener.onConnected();
}
}
public synchronized void onServiceDisconnected(ComponentName name) {
myInterface = null;
}
public synchronized boolean isConnected() {
return myInterface != null;
}
private synchronized void checkConnection() throws ApiException {
if (myInterface == null) {
throw new ApiException("Not connected to FBReader");
}
}
private synchronized ApiObject request(int method, ApiObject[] params) throws ApiException {
checkConnection();
try {
final ApiObject object = myInterface.request(method, params);
if (object instanceof ApiObject.Error) {
throw new ApiException(((ApiObject.Error)object).Message);
}
return object;
} catch (android.os.RemoteException e) {
throw new ApiException(e);
}
}
private synchronized List<ApiObject> requestList(int method, ApiObject[] params) throws ApiException {
checkConnection();
try {
final List<ApiObject> list = myInterface.requestList(method, params);
for (ApiObject object : list) {
if (object instanceof ApiObject.Error) {
throw new ApiException(((ApiObject.Error)object).Message);
}
}
return list;
} catch (android.os.RemoteException e) {
throw new ApiException(e);
}
}
private String requestString(int method, ApiObject[] params) throws ApiException {
final ApiObject object = request(method, params);
if (!(object instanceof ApiObject.String)) {
throw new ApiException("Cannot cast return type of method " + method + " to String");
}
return ((ApiObject.String)object).Value;
}
private Date requestDate(int method, ApiObject[] params) throws ApiException {
final ApiObject object = request(method, params);
if (!(object instanceof ApiObject.Date)) {
throw new ApiException("Cannot cast return type of method " + method + " to Date");
}
return ((ApiObject.Date)object).Value;
}
private int requestInt(int method, ApiObject[] params) throws ApiException {
final ApiObject object = request(method, params);
if (!(object instanceof ApiObject.Integer)) {
throw new ApiException("Cannot cast return type of method " + method + " to int");
}
return ((ApiObject.Integer)object).Value;
}
private float requestFloat(int method, ApiObject[] params) throws ApiException {
final ApiObject object = request(method, params);
if (!(object instanceof ApiObject.Float)) {
throw new ApiException("Cannot cast return type of method " + method + " to float");
}
return ((ApiObject.Float)object).Value;
}
private boolean requestBoolean(int method, ApiObject[] params) throws ApiException {
final ApiObject object = request(method, params);
if (!(object instanceof ApiObject.Boolean)) {
throw new ApiException("Cannot cast return type of method " + method + " to boolean");
}
return ((ApiObject.Boolean)object).Value;
}
private TextPosition requestTextPosition(int method, ApiObject[] params) throws ApiException {
final ApiObject object = request(method, params);
if (!(object instanceof TextPosition)) {
throw new ApiException("Cannot cast return type of method " + method + " to TextPosition");
}
return (TextPosition)object;
}
private <T extends Parcelable> T requestParcelable(int method, ApiObject[] params) throws ApiException {
final ApiObject object = request(method, params);
if (!(object instanceof ApiObject.Parcelable)) {
throw new ApiException("Cannot cast return type of method " + method + " to Parcelable");
}
return (T)((ApiObject.Parcelable)object).Value;
}
private List<String> requestStringList(int method, ApiObject[] params) throws ApiException {
final List<ApiObject> list = requestList(method, params);
final ArrayList<String> stringList = new ArrayList<String>(list.size());
for (ApiObject object : list) {
if (!(object instanceof ApiObject.String)) {
throw new ApiException("Cannot cast an element returned from method " + method + " to String");
}
stringList.add(((ApiObject.String)object).Value);
}
return stringList;
}
private <T extends Serializable> List<T> requestSerializableList(int method, ApiObject[] params) throws ApiException {
final List<ApiObject> list = requestList(method, params);
final ArrayList<T> serializableList = new ArrayList<T>(list.size());
for (ApiObject object : list) {
if (!(object instanceof ApiObject.Serializable)) {
throw new ApiException("Cannot cast an element returned from method " + method + " to Serializable");
}
serializableList.add((T)((ApiObject.Serializable)object).Value);
}
return serializableList;
}
private List<Integer> requestIntegerList(int method, ApiObject[] params) throws ApiException {
final List<ApiObject> list = requestList(method, params);
final ArrayList<Integer> intList = new ArrayList<Integer>(list.size());
for (ApiObject object : list) {
if (!(object instanceof ApiObject.Integer)) {
throw new ApiException("Cannot cast an element returned from method " + method + " to Integer");
}
intList.add(((ApiObject.Integer)object).Value);
}
return intList;
}
private static final ApiObject[] EMPTY_PARAMETERS = new ApiObject[0];
private static ApiObject[] envelope(String value) {
return new ApiObject[] { ApiObject.envelope(value) };
}
private static ApiObject[] envelope(int value) {
return new ApiObject[] { ApiObject.envelope(value) };
}
private static ApiObject[] envelope(float value) {
return new ApiObject[] { ApiObject.envelope(value) };
}
private static ApiObject[] envelope(long value) {
return new ApiObject[] { ApiObject.envelope(value) };
}
private static ApiObject[] envelope(List<String> value) {
final ApiObject[] objects = new ApiObject[value.size()];
int index = 0;
for (String s : value) {
objects[index++] = ApiObject.envelope(s);
}
return objects;
}
private static ApiObject[] envelope(String[] value) {
final ApiObject[] objects = new ApiObject[value.length];
int index = 0;
for (String s : value) {
objects[index++] = ApiObject.envelope(s);
}
return objects;
}
// information about fbreader
public String getFBReaderVersion() throws ApiException {
return requestString(GET_FBREADER_VERSION, EMPTY_PARAMETERS);
}
// preferences information
public List<String> getOptionGroups() throws ApiException {
return requestStringList(LIST_OPTION_GROUPS, EMPTY_PARAMETERS);
}
public List<String> getOptionNames(String group) throws ApiException {
return requestStringList(LIST_OPTION_NAMES, envelope(group));
}
public String getOptionValue(String group, String name) throws ApiException {
return requestString(
GET_OPTION_VALUE,
new ApiObject[] { ApiObject.envelope(group), ApiObject.envelope(name) }
);
}
public void setOptionValue(String group, String name, String value) throws ApiException {
request(
SET_OPTION_VALUE,
new ApiObject[] { ApiObject.envelope(group), ApiObject.envelope(name), ApiObject.envelope(value) }
);
}
public String getBookLanguage() throws ApiException {
return requestString(GET_BOOK_LANGUAGE, EMPTY_PARAMETERS);
}
public String getBookTitle() throws ApiException {
return requestString(GET_BOOK_TITLE, EMPTY_PARAMETERS);
}
public List<String> getBookTags() throws ApiException {
return requestStringList(LIST_BOOK_TAGS, EMPTY_PARAMETERS);
}
public String getBookFilePath() throws ApiException {
return requestString(GET_BOOK_FILE_PATH, EMPTY_PARAMETERS);
}
public String getBookHash() throws ApiException {
return requestString(GET_BOOK_HASH, EMPTY_PARAMETERS);
}
public List<String> getBookAuthors() throws ApiException {
return requestStringList(LIST_BOOK_AUTHORS, EMPTY_PARAMETERS);
}
public float getBookProgress() throws ApiException {
return requestFloat(GET_BOOK_PROGRESS, EMPTY_PARAMETERS);
}
public String getBookUniqueId() throws ApiException {
return requestString(GET_BOOK_UNIQUE_ID, EMPTY_PARAMETERS);
}
public Date getBookLastTurningTime() throws ApiException {
return requestDate(GET_BOOK_LAST_TURNING_TIME, EMPTY_PARAMETERS);
}
public String getBookLanguage(long id) throws ApiException {
return requestString(GET_BOOK_LANGUAGE, envelope(id));
}
public String getBookTitle(long id) throws ApiException {
return requestString(GET_BOOK_TITLE, envelope(id));
}
public List<String> getBookTags(long id) throws ApiException {
return requestStringList(LIST_BOOK_TAGS, envelope(id));
}
public String getBookFilePath(long id) throws ApiException {
return requestString(GET_BOOK_FILE_PATH, envelope(id));
}
public String getBookHash(long id) throws ApiException {
return requestString(GET_BOOK_HASH, envelope(id));
}
public String getBookUniqueId(long id) throws ApiException {
return requestString(GET_BOOK_UNIQUE_ID, envelope(id));
}
public Date getBookLastTurningTime(long id) throws ApiException {
return requestDate(GET_BOOK_LAST_TURNING_TIME, envelope(id));
}
public TextPosition getPageStart() throws ApiException {
return requestTextPosition(GET_PAGE_START, EMPTY_PARAMETERS);
}
public TextPosition getPageEnd() throws ApiException {
return requestTextPosition(GET_PAGE_END, EMPTY_PARAMETERS);
}
public boolean isPageEndOfSection() throws ApiException {
return requestBoolean(IS_PAGE_END_OF_SECTION, EMPTY_PARAMETERS);
}
public boolean isPageEndOfText() throws ApiException {
return requestBoolean(IS_PAGE_END_OF_TEXT, EMPTY_PARAMETERS);
}
public int getParagraphsNumber() throws ApiException {
return requestInt(GET_PARAGRAPHS_NUMBER, EMPTY_PARAMETERS);
}
public String getParagraphText(int paragraphIndex) throws ApiException {
return requestString(GET_PARAGRAPH_TEXT, envelope(paragraphIndex));
}
public int getParagraphElementsCount(int paragraphIndex) throws ApiException {
return requestInt(GET_PARAGRAPH_ELEMENTS_COUNT, envelope(paragraphIndex));
}
public List<String> getParagraphWords(int paragraphIndex) throws ApiException {
return requestStringList(GET_PARAGRAPH_WORDS, envelope(paragraphIndex));
}
public List<Integer> getParagraphWordIndices(int paragraphIndex) throws ApiException {
return requestIntegerList(GET_PARAGRAPH_WORD_INDICES, envelope(paragraphIndex));
}
public void setPageStart(TextPosition position) throws ApiException {
request(SET_PAGE_START, new ApiObject[] { position });
}
public void highlightArea(TextPosition start, TextPosition end) throws ApiException {
request(HIGHLIGHT_AREA, new ApiObject[] { start, end });
}
public void clearHighlighting() throws ApiException {
request(CLEAR_HIGHLIGHTING, EMPTY_PARAMETERS);
}
public int getBottomMargin() throws ApiException {
return requestInt(GET_BOTTOM_MARGIN, EMPTY_PARAMETERS);
}
public void setBottomMargin(int value) throws ApiException {
request(SET_BOTTOM_MARGIN, new ApiObject[] { ApiObject.envelope(value) });
}
public int getTopMargin() throws ApiException {
return requestInt(GET_TOP_MARGIN, EMPTY_PARAMETERS);
}
public void setTopMargin(int value) throws ApiException {
request(SET_TOP_MARGIN, new ApiObject[] { ApiObject.envelope(value) });
}
public int getLeftMargin() throws ApiException {
return requestInt(GET_LEFT_MARGIN, EMPTY_PARAMETERS);
}
public void setLeftMargin(int value) throws ApiException {
request(SET_LEFT_MARGIN, new ApiObject[] { ApiObject.envelope(value) });
}
public int getRightMargin() throws ApiException {
return requestInt(GET_RIGHT_MARGIN, EMPTY_PARAMETERS);
}
public void setRightMargin(int value) throws ApiException {
request(SET_RIGHT_MARGIN, new ApiObject[] { ApiObject.envelope(value) });
}
// action control
public String getKeyAction(int key, boolean longPress) throws ApiException {
return requestString(GET_KEY_ACTION, new ApiObject[] {
ApiObject.envelope(key),
ApiObject.envelope(longPress)
});
}
public void setKeyAction(int key, boolean longPress, String action) throws ApiException {
request(SET_KEY_ACTION, new ApiObject[] {
ApiObject.envelope(key),
ApiObject.envelope(longPress),
ApiObject.envelope(action)
});
}
public List<String> listActions() throws ApiException {
return requestStringList(LIST_ACTIONS, EMPTY_PARAMETERS);
}
public List<String> listActionNames(List<String> actions) throws ApiException {
return requestStringList(LIST_ACTION_NAMES, envelope(actions));
}
public List<String> listZoneMaps() throws ApiException {
return requestStringList(LIST_ZONEMAPS, EMPTY_PARAMETERS);
}
public String getZoneMap() throws ApiException {
return requestString(GET_ZONEMAP, EMPTY_PARAMETERS);
}
public void setZoneMap(String name) throws ApiException {
request(SET_ZONEMAP, envelope(name));
}
public int getZoneMapHeight(String name) throws ApiException {
return requestInt(GET_ZONEMAP_HEIGHT, envelope(name));
}
public int getZoneMapWidth(String name) throws ApiException {
return requestInt(GET_ZONEMAP_WIDTH, envelope(name));
}
public void createZoneMap(String name, int width, int height) throws ApiException {
request(CREATE_ZONEMAP, new ApiObject[] {
ApiObject.envelope(name),
ApiObject.envelope(width),
ApiObject.envelope(height)
});
}
public boolean isZoneMapCustom(String name) throws ApiException {
return requestBoolean(IS_ZONEMAP_CUSTOM, envelope(name));
}
public void deleteZoneMap(String name) throws ApiException {
request(DELETE_ZONEMAP, envelope(name));
}
public String getTapZoneAction(String name, int h, int v, boolean singleTap) throws ApiException {
return requestString(GET_TAPZONE_ACTION, new ApiObject[] {
ApiObject.envelope(name),
ApiObject.envelope(h),
ApiObject.envelope(v),
ApiObject.envelope(singleTap)
});
}
public String getTapActionByCoordinates(String name, int x, int y, int width, int height, String tap) throws ApiException {
return requestString(GET_TAP_ACTION_BY_COORDINATES, new ApiObject[] {
ApiObject.envelope(name),
ApiObject.envelope(x),
ApiObject.envelope(y),
ApiObject.envelope(width),
ApiObject.envelope(height),
ApiObject.envelope(tap)
});
}
public void setTapZoneAction(String name, int h, int v, boolean singleTap, String action) throws ApiException {
request(SET_TAPZONE_ACTION, new ApiObject[] {
ApiObject.envelope(name),
ApiObject.envelope(h),
ApiObject.envelope(v),
ApiObject.envelope(singleTap),
ApiObject.envelope(action)
});
}
public List<MenuNode> getMainMenuContent() throws ApiException {
return requestSerializableList(GET_MAIN_MENU_CONTENT, EMPTY_PARAMETERS);
}
public String getResourceString(String ... keys) throws ApiException {
return requestString(GET_RESOURCE_STRING, envelope(keys));
}
public Bitmap getBitmap(int resourceId) throws ApiException {
return requestParcelable(GET_BITMAP, envelope(resourceId));
}
}

View file

@ -0,0 +1,17 @@
/*
* This code is in the public domain.
*/
package org.geometerplus.android.fbreader.api;
public class ApiException extends Exception {
private static final long serialVersionUID = -6316637693779831867L;
ApiException(String message) {
super(message);
}
ApiException(Exception parent) {
super(parent);
}
}

View file

@ -0,0 +1,13 @@
/*
* This code is in the public domain.
*/
package org.geometerplus.android.fbreader.api;
import org.geometerplus.android.fbreader.api.ApiObject;
interface ApiInterface {
ApiObject request(int method, in ApiObject[] parameters);
List<ApiObject> requestList(int method, in ApiObject[] parameters);
Map requestMap(int method, in ApiObject[] parameters);
}

View file

@ -0,0 +1,12 @@
/*
* This code is in the public domain.
*/
package org.geometerplus.android.fbreader.api;
public interface ApiListener {
String EVENT_READ_MODE_OPENED = "startReading";
String EVENT_READ_MODE_CLOSED = "stopReading";
void onEvent(int event);
}

View file

@ -0,0 +1,85 @@
/*
* This code is in the public domain.
*/
package org.geometerplus.android.fbreader.api;
interface ApiMethods {
// fbreader information
int GET_FBREADER_VERSION = 1;
// library information
// network library information
// bookmarks information
// preferences
int LIST_OPTION_GROUPS = 401;
int LIST_OPTION_NAMES = 402;
int GET_OPTION_VALUE = 403;
int SET_OPTION_VALUE = 404;
// book information
int GET_BOOK_LANGUAGE = 501;
int GET_BOOK_TITLE = 502;
int LIST_BOOK_AUTHORS = 503;
int LIST_BOOK_TAGS = 504;
int GET_BOOK_FILE_PATH = 505;
int GET_BOOK_HASH = 506;
int GET_BOOK_UNIQUE_ID = 507;
int GET_BOOK_LAST_TURNING_TIME = 508;
// book information: read progress
int GET_BOOK_PROGRESS = 509;
// text information
int GET_PARAGRAPHS_NUMBER = 601;
int GET_PARAGRAPH_ELEMENTS_COUNT = 602;
int GET_PARAGRAPH_TEXT = 603;
int GET_PARAGRAPH_WORDS = 604;
int GET_PARAGRAPH_WORD_INDICES = 605;
// page information
int GET_PAGE_START = 701;
int GET_PAGE_END = 702;
int IS_PAGE_END_OF_TEXT = 703;
int IS_PAGE_END_OF_SECTION = 704;
// view management
int SET_PAGE_START = 801;
int HIGHLIGHT_AREA = 802;
int CLEAR_HIGHLIGHTING = 803;
int GET_BOTTOM_MARGIN = 804;
int SET_BOTTOM_MARGIN = 805;
int GET_TOP_MARGIN = 806;
int SET_TOP_MARGIN = 807;
int GET_LEFT_MARGIN = 808;
int SET_LEFT_MARGIN = 809;
int GET_RIGHT_MARGIN = 810;
int SET_RIGHT_MARGIN = 811;
// action control
int LIST_ACTIONS = 901;
int LIST_ACTION_NAMES = 902;
int GET_KEY_ACTION = 911;
int SET_KEY_ACTION = 912;
int LIST_ZONEMAPS = 921;
int GET_ZONEMAP = 922;
int SET_ZONEMAP = 923;
int GET_ZONEMAP_HEIGHT = 924;
int GET_ZONEMAP_WIDTH = 925;
int CREATE_ZONEMAP = 926;
int IS_ZONEMAP_CUSTOM = 927;
int DELETE_ZONEMAP = 928;
int GET_TAPZONE_ACTION = 931;
int SET_TAPZONE_ACTION = 932;
int GET_TAP_ACTION_BY_COORDINATES = 933;
// for format plugins
int GET_MAIN_MENU_CONTENT = 1001;
int GET_RESOURCE_STRING = 1002;
int GET_BITMAP = 1003;
}

View file

@ -0,0 +1,7 @@
/*
* This code is in the public domain.
*/
package org.geometerplus.android.fbreader.api;
parcelable ApiObject;

View file

@ -0,0 +1,309 @@
/*
* This code is in the public domain.
*/
package org.geometerplus.android.fbreader.api;
import java.util.List;
import java.util.ArrayList;
import android.os.Parcel;
import android.os.Parcelable;
public abstract class ApiObject implements Parcelable {
protected static interface Type {
int ERROR = -1;
int VOID = 0;
int INT = 1;
int STRING = 2;
int BOOLEAN = 3;
int DATE = 4;
int LONG = 5;
int FLOAT = 6;
int TEXT_POSITION = 10;
int SERIALIZABLE = 20;
int PARCELABALE = 21;
}
static class Void extends ApiObject {
static Void Instance = new Void();
private Void() {
}
@Override
protected int type() {
return Type.VOID;
}
}
static class Integer extends ApiObject {
final int Value;
Integer(int value) {
Value = value;
}
@Override
protected int type() {
return Type.INT;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeInt(Value);
}
}
static class Long extends ApiObject {
final long Value;
Long(long value) {
Value = value;
}
@Override
protected int type() {
return Type.LONG;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeLong(Value);
}
}
static class Float extends ApiObject {
final float Value;
Float(float value) {
Value = value;
}
@Override
protected int type() {
return Type.FLOAT;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeFloat(Value);
}
}
static class Boolean extends ApiObject {
final boolean Value;
Boolean(boolean value) {
Value = value;
}
@Override
protected int type() {
return Type.BOOLEAN;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeByte((byte)(Value ? 1 : 0));
}
}
static class Date extends ApiObject {
final java.util.Date Value;
Date(java.util.Date value) {
Value = value;
}
@Override
protected int type() {
return Type.DATE;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeLong(Value.getTime());
}
}
static class String extends ApiObject {
final java.lang.String Value;
String(java.lang.String value) {
Value = value;
}
@Override
protected int type() {
return Type.STRING;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeString(Value);
}
}
static class Serializable extends ApiObject {
final java.io.Serializable Value;
Serializable(java.io.Serializable value) {
Value = value;
}
@Override
protected int type() {
return Type.SERIALIZABLE;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeSerializable(Value);
}
}
static class Parcelable extends ApiObject {
final android.os.Parcelable Value;
Parcelable(android.os.Parcelable value) {
Value = value;
}
@Override
protected int type() {
return Type.PARCELABALE;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeParcelable(Value, 0);
}
}
static class Error extends ApiObject {
final java.lang.String Message;
Error(java.lang.String message) {
Message = message;
}
@Override
protected int type() {
return Type.ERROR;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeString(Message);
}
}
static ApiObject envelope(int value) {
return new Integer(value);
}
static ApiObject envelope(long value) {
return new Long(value);
}
static ApiObject envelope(float value) {
return new Float(value);
}
static ApiObject envelope(boolean value) {
return new Boolean(value);
}
static ApiObject envelope(java.lang.String value) {
return new String(value);
}
static ApiObject envelope(java.util.Date value) {
return new Date(value);
}
static ApiObject envelope(android.os.Parcelable value) {
return new Parcelable(value);
}
static List<ApiObject> envelopeStringList(List<java.lang.String> values) {
final ArrayList<ApiObject> objects = new ArrayList<ApiObject>(values.size());
for (java.lang.String v : values) {
objects.add(new String(v));
}
return objects;
}
static List<ApiObject> envelopeSerializableList(List<? extends java.io.Serializable> values) {
final ArrayList<ApiObject> objects = new ArrayList<ApiObject>(values.size());
for (java.io.Serializable v : values) {
objects.add(new Serializable(v));
}
return objects;
}
static List<ApiObject> envelopeIntegerList(List<java.lang.Integer> values) {
final ArrayList<ApiObject> objects = new ArrayList<ApiObject>(values.size());
for (java.lang.Integer v : values) {
objects.add(new Integer(v));
}
return objects;
}
abstract protected int type();
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(type());
}
public static final Parcelable.Creator<ApiObject> CREATOR =
new Parcelable.Creator<ApiObject>() {
public ApiObject createFromParcel(Parcel parcel) {
final int code = parcel.readInt();
switch (code) {
default:
return new Error("Unknown object code: " + code);
case Type.ERROR:
return new Error(parcel.readString());
case Type.VOID:
return Void.Instance;
case Type.INT:
return new Integer(parcel.readInt());
case Type.LONG:
return new Long(parcel.readLong());
case Type.FLOAT:
return new Float(parcel.readFloat());
case Type.BOOLEAN:
return new Boolean(parcel.readByte() == 1);
case Type.DATE:
return new Date(new java.util.Date(parcel.readLong()));
case Type.STRING:
return new String(parcel.readString());
case Type.TEXT_POSITION:
return new TextPosition(parcel.readInt(), parcel.readInt(), parcel.readInt());
case Type.SERIALIZABLE:
return new Serializable(parcel.readSerializable());
case Type.PARCELABALE:
return new Parcelable(parcel.readParcelable(null));
}
}
public ApiObject[] newArray(int size) {
return new ApiObject[size];
}
};
}

View file

@ -0,0 +1,673 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.api;
import java.util.*;
import android.content.*;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import org.geometerplus.zlibrary.core.application.ZLKeyBindings;
import org.geometerplus.zlibrary.core.library.ZLibrary;
import org.geometerplus.zlibrary.core.options.Config;
import org.geometerplus.zlibrary.core.options.ZLStringOption;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.util.RationalNumber;
import org.geometerplus.zlibrary.text.view.*;
import org.geometerplus.fbreader.book.*;
import org.geometerplus.fbreader.fbreader.*;
import org.geometerplus.android.fbreader.*;
public class ApiServerImplementation extends ApiInterface.Stub implements Api, ApiMethods {
public static void sendEvent(ContextWrapper context, String eventType) {
context.sendBroadcast(
new Intent(FBReaderIntents.Action.API_CALLBACK)
.putExtra(ApiClientImplementation.EVENT_TYPE, eventType)
);
}
private final Context myContext;
private volatile FBReaderApp myReader;
private final ZLKeyBindings myBindings = new ZLKeyBindings();
ApiServerImplementation(Context context) {
myContext = context;
}
private synchronized FBReaderApp getReader() {
if (myReader == null) {
myReader = (FBReaderApp)FBReaderApp.Instance();
}
return myReader;
}
private ApiObject.Error unsupportedMethodError(int method) {
return new ApiObject.Error("Unsupported method code: " + method);
}
private ApiObject.Error exceptionInMethodError(int method, Throwable e) {
return new ApiObject.Error("Exception in method " + method + ": " + e);
}
public ApiObject request(int method, ApiObject[] parameters) {
try {
switch (method) {
case GET_FBREADER_VERSION:
return ApiObject.envelope(getFBReaderVersion());
case GET_OPTION_VALUE:
return ApiObject.envelope(getOptionValue(
((ApiObject.String)parameters[0]).Value,
((ApiObject.String)parameters[1]).Value
));
case SET_OPTION_VALUE:
setOptionValue(
((ApiObject.String)parameters[0]).Value,
((ApiObject.String)parameters[1]).Value,
((ApiObject.String)parameters[2]).Value
);
return ApiObject.Void.Instance;
case GET_BOOK_LANGUAGE:
if (parameters.length == 0) {
return ApiObject.envelope(getBookLanguage());
} else {
return ApiObject.envelope(getBookLanguage(((ApiObject.Long)parameters[0]).Value));
}
case GET_BOOK_TITLE:
if (parameters.length == 0) {
return ApiObject.envelope(getBookTitle());
} else {
return ApiObject.envelope(getBookTitle(((ApiObject.Long)parameters[0]).Value));
}
case GET_BOOK_FILE_PATH:
if (parameters.length == 0) {
return ApiObject.envelope(getBookFilePath());
} else {
return ApiObject.envelope(getBookFilePath(((ApiObject.Long)parameters[0]).Value));
}
case GET_BOOK_HASH:
if (parameters.length == 0) {
return ApiObject.envelope(getBookHash());
} else {
return ApiObject.envelope(getBookHash(((ApiObject.Long)parameters[0]).Value));
}
case GET_BOOK_UNIQUE_ID:
if (parameters.length == 0) {
return ApiObject.envelope(getBookUniqueId());
} else {
return ApiObject.envelope(getBookUniqueId(((ApiObject.Long)parameters[0]).Value));
}
case GET_BOOK_LAST_TURNING_TIME:
if (parameters.length == 0) {
return ApiObject.envelope(getBookLastTurningTime());
} else {
return ApiObject.envelope(getBookLastTurningTime(((ApiObject.Long)parameters[0]).Value));
}
case GET_BOOK_PROGRESS:
return ApiObject.envelope(getBookProgress());
case GET_PARAGRAPHS_NUMBER:
return ApiObject.envelope(getParagraphsNumber());
case GET_PARAGRAPH_ELEMENTS_COUNT:
return ApiObject.envelope(getParagraphElementsCount(
((ApiObject.Integer)parameters[0]).Value
));
case GET_PARAGRAPH_TEXT:
return ApiObject.envelope(getParagraphText(
((ApiObject.Integer)parameters[0]).Value
));
case GET_PAGE_START:
return getPageStart();
case GET_PAGE_END:
return getPageEnd();
case IS_PAGE_END_OF_SECTION:
return ApiObject.envelope(isPageEndOfSection());
case IS_PAGE_END_OF_TEXT:
return ApiObject.envelope(isPageEndOfText());
case SET_PAGE_START:
setPageStart((TextPosition)parameters[0]);
return ApiObject.Void.Instance;
case HIGHLIGHT_AREA:
{
highlightArea((TextPosition)parameters[0], (TextPosition)parameters[1]);
return ApiObject.Void.Instance;
}
case CLEAR_HIGHLIGHTING:
clearHighlighting();
return ApiObject.Void.Instance;
case GET_BOTTOM_MARGIN:
return ApiObject.envelope(getBottomMargin());
case SET_BOTTOM_MARGIN:
setBottomMargin(((ApiObject.Integer)parameters[0]).Value);
return ApiObject.Void.Instance;
case GET_TOP_MARGIN:
return ApiObject.envelope(getTopMargin());
case SET_TOP_MARGIN:
setTopMargin(((ApiObject.Integer)parameters[0]).Value);
return ApiObject.Void.Instance;
case GET_LEFT_MARGIN:
return ApiObject.envelope(getLeftMargin());
case SET_LEFT_MARGIN:
setLeftMargin(((ApiObject.Integer)parameters[0]).Value);
return ApiObject.Void.Instance;
case GET_RIGHT_MARGIN:
return ApiObject.envelope(getRightMargin());
case SET_RIGHT_MARGIN:
setRightMargin(((ApiObject.Integer)parameters[0]).Value);
return ApiObject.Void.Instance;
case GET_KEY_ACTION:
return ApiObject.envelope(getKeyAction(
((ApiObject.Integer)parameters[0]).Value,
((ApiObject.Boolean)parameters[1]).Value
));
case SET_KEY_ACTION:
setKeyAction(
((ApiObject.Integer)parameters[0]).Value,
((ApiObject.Boolean)parameters[1]).Value,
((ApiObject.String)parameters[2]).Value
);
return ApiObject.Void.Instance;
case GET_ZONEMAP:
return ApiObject.envelope(getZoneMap());
case SET_ZONEMAP:
setZoneMap(((ApiObject.String)parameters[0]).Value);
return ApiObject.Void.Instance;
case GET_ZONEMAP_HEIGHT:
return ApiObject.envelope(getZoneMapHeight(((ApiObject.String)parameters[0]).Value));
case GET_ZONEMAP_WIDTH:
return ApiObject.envelope(getZoneMapWidth(((ApiObject.String)parameters[0]).Value));
case GET_TAPZONE_ACTION:
return ApiObject.envelope(getTapZoneAction(
((ApiObject.String)parameters[0]).Value,
((ApiObject.Integer)parameters[1]).Value,
((ApiObject.Integer)parameters[2]).Value,
((ApiObject.Boolean)parameters[3]).Value
));
case GET_TAP_ACTION_BY_COORDINATES:
return ApiObject.envelope(getTapActionByCoordinates(
((ApiObject.String)parameters[0]).Value,
((ApiObject.Integer)parameters[1]).Value,
((ApiObject.Integer)parameters[2]).Value,
((ApiObject.Integer)parameters[3]).Value,
((ApiObject.Integer)parameters[4]).Value,
((ApiObject.String)parameters[5]).Value
));
case SET_TAPZONE_ACTION:
setTapZoneAction(
((ApiObject.String)parameters[0]).Value,
((ApiObject.Integer)parameters[1]).Value,
((ApiObject.Integer)parameters[2]).Value,
((ApiObject.Boolean)parameters[3]).Value,
((ApiObject.String)parameters[4]).Value
);
return ApiObject.Void.Instance;
case CREATE_ZONEMAP:
createZoneMap(
((ApiObject.String)parameters[0]).Value,
((ApiObject.Integer)parameters[1]).Value,
((ApiObject.Integer)parameters[2]).Value
);
return ApiObject.Void.Instance;
case IS_ZONEMAP_CUSTOM:
return ApiObject.envelope(isZoneMapCustom(
((ApiObject.String)parameters[0]).Value
));
case DELETE_ZONEMAP:
deleteZoneMap(((ApiObject.String)parameters[0]).Value);
return ApiObject.Void.Instance;
case GET_RESOURCE_STRING:
{
final String[] stringParams = new String[parameters.length];
for (int i = 0; i < parameters.length; ++i) {
stringParams[i] = ((ApiObject.String)parameters[i]).Value;
}
return ApiObject.envelope(getResourceString(stringParams));
}
case GET_BITMAP:
return ApiObject.envelope(getBitmap(((ApiObject.Integer)parameters[0]).Value));
default:
return unsupportedMethodError(method);
}
} catch (Throwable e) {
return new ApiObject.Error("Exception in method " + method + ": " + e);
}
}
public List<ApiObject> requestList(int method, ApiObject[] parameters) {
try {
switch (method) {
case LIST_OPTION_GROUPS:
return ApiObject.envelopeStringList(getOptionGroups());
case LIST_OPTION_NAMES:
return ApiObject.envelopeStringList(getOptionNames(
((ApiObject.String)parameters[0]).Value
));
case LIST_BOOK_TAGS:
return ApiObject.envelopeStringList(getBookTags());
case LIST_BOOK_AUTHORS:
return ApiObject.envelopeStringList(getBookAuthors());
case LIST_ACTIONS:
return ApiObject.envelopeStringList(listActions());
case LIST_ACTION_NAMES:
{
final ArrayList<String> actions = new ArrayList<String>(parameters.length);
for (ApiObject o : parameters) {
actions.add(((ApiObject.String)o).Value);
}
return ApiObject.envelopeStringList(listActionNames(actions));
}
case LIST_ZONEMAPS:
return ApiObject.envelopeStringList(listZoneMaps());
case GET_PARAGRAPH_WORDS:
return ApiObject.envelopeStringList(getParagraphWords(
((ApiObject.Integer)parameters[0]).Value
));
case GET_PARAGRAPH_WORD_INDICES:
return ApiObject.envelopeIntegerList(getParagraphWordIndices(
((ApiObject.Integer)parameters[0]).Value
));
case GET_MAIN_MENU_CONTENT:
return ApiObject.envelopeSerializableList(getMainMenuContent());
default:
return Collections.<ApiObject>singletonList(unsupportedMethodError(method));
}
} catch (Throwable e) {
return Collections.<ApiObject>singletonList(exceptionInMethodError(method, e));
}
}
private Map<ApiObject,ApiObject> errorMap(ApiObject.Error error) {
return Collections.<ApiObject,ApiObject>singletonMap(error, error);
}
public Map<ApiObject,ApiObject> requestMap(int method, ApiObject[] parameters) {
try {
switch (method) {
default:
return errorMap(unsupportedMethodError(method));
}
} catch (Throwable e) {
return errorMap(exceptionInMethodError(method, e));
}
}
// information about fbreader
public String getFBReaderVersion() {
return ZLibrary.Instance().getVersionName();
}
// preferences information
public List<String> getOptionGroups() {
return Config.Instance().listGroups();
}
public List<String> getOptionNames(String group) {
return Config.Instance().listNames(group);
}
public String getOptionValue(String group, String name) {
return new ZLStringOption(group, name, null).getValue();
}
public void setOptionValue(String group, String name, String value) {
new ZLStringOption(group, name, null).setValue(value);
}
public String getBookLanguage() {
final Book book = getReader().getCurrentBook();
return book != null ? book.getLanguage() : null;
}
public String getBookTitle() {
final Book book = getReader().getCurrentBook();
return book != null ? book.getTitle() : null;
}
public List<String> getBookTags() {
// TODO: implement
return Collections.emptyList();
}
public float getBookProgress() {
final Book book = getReader().getCurrentBook();
if (book == null) {
return -1.0f;
}
final RationalNumber progress = book.getProgress();
return progress != null ? progress.toFloat() : -1.0f;
}
public List<String> getBookAuthors() {
final Book book = getReader().getCurrentBook();
if (book == null) {
return null;
}
final List<Author> authors = book.authors();
final List<String> authorNames = new ArrayList<String>(authors.size());
for (Author a : authors) {
authorNames.add(a.DisplayName);
}
return authorNames;
}
public String getBookFilePath() {
final Book book = getReader().getCurrentBook();
return book != null ? book.getPath() : null;
}
public String getBookHash() {
final Book book = getReader().getCurrentBook();
if (book == null) {
return null;
}
final UID uid = BookUtil.createUid(book, "SHA-256");
return uid != null ? uid.Id : null;
}
public String getBookUniqueId() {
// TODO: implement
return null;
}
public Date getBookLastTurningTime() {
// TODO: implement
return null;
}
public String getBookLanguage(long id) {
// TODO: implement
return null;
}
public String getBookTitle(long id) {
// TODO: implement
return null;
}
public List<String> getBookTags(long id) {
// TODO: implement
return Collections.emptyList();
}
public String getBookFilePath(long id) {
// TODO: implement
return null;
}
public String getBookHash(long id) {
// TODO: implement
return null;
}
public String getBookUniqueId(long id) {
// TODO: implement
return null;
}
public Date getBookLastTurningTime(long id) {
// TODO: implement
return null;
}
// page information
public TextPosition getPageStart() {
return getTextPosition(getReader().getTextView().getStartCursor());
}
public TextPosition getPageEnd() {
return getTextPosition(getReader().getTextView().getEndCursor());
}
public boolean isPageEndOfSection() {
final ZLTextWordCursor cursor = getReader().getTextView().getEndCursor();
return cursor.isEndOfParagraph() && cursor.getParagraphCursor().isEndOfSection();
}
public boolean isPageEndOfText() {
final ZLTextWordCursor cursor = getReader().getTextView().getEndCursor();
return cursor.isEndOfParagraph() && cursor.getParagraphCursor().isLast();
}
private TextPosition getTextPosition(ZLTextWordCursor cursor) {
return new TextPosition(
cursor.getParagraphIndex(),
cursor.getElementIndex(),
cursor.getCharIndex()
);
}
private ZLTextFixedPosition getZLTextPosition(TextPosition position) {
return new ZLTextFixedPosition(
position.ParagraphIndex,
position.ElementIndex,
position.CharIndex
);
}
// manage view
public void setPageStart(TextPosition position) {
getReader().getTextView().gotoPosition(position.ParagraphIndex, position.ElementIndex, position.CharIndex);
getReader().getViewWidget().repaint();
getReader().storePosition();
}
public void highlightArea(TextPosition start, TextPosition end) {
getReader().getTextView().highlight(
getZLTextPosition(start),
getZLTextPosition(end)
);
}
public void clearHighlighting() {
getReader().getTextView().clearHighlighting();
}
public int getBottomMargin() {
return getReader().ViewOptions.BottomMargin.getValue();
}
public void setBottomMargin(int value) {
getReader().ViewOptions.BottomMargin.setValue(value);
}
public int getTopMargin() {
return getReader().ViewOptions.TopMargin.getValue();
}
public void setTopMargin(int value) {
getReader().ViewOptions.TopMargin.setValue(value);
}
public int getLeftMargin() {
return getReader().ViewOptions.LeftMargin.getValue();
}
public void setLeftMargin(int value) {
getReader().ViewOptions.LeftMargin.setValue(value);
}
public int getRightMargin() {
return getReader().ViewOptions.RightMargin.getValue();
}
public void setRightMargin(int value) {
getReader().ViewOptions.RightMargin.setValue(value);
}
public int getParagraphsNumber() {
return getReader().Model.getTextModel().getParagraphsNumber();
}
public int getParagraphElementsCount(int paragraphIndex) {
final ZLTextWordCursor cursor = new ZLTextWordCursor(getReader().getTextView().getStartCursor());
cursor.moveToParagraph(paragraphIndex);
cursor.moveToParagraphEnd();
return cursor.getElementIndex();
}
public String getParagraphText(int paragraphIndex) {
final StringBuffer sb = new StringBuffer();
final ZLTextWordCursor cursor = new ZLTextWordCursor(getReader().getTextView().getStartCursor());
cursor.moveToParagraph(paragraphIndex);
cursor.moveToParagraphStart();
while (!cursor.isEndOfParagraph()) {
ZLTextElement element = cursor.getElement();
if (element instanceof ZLTextWord) {
sb.append(element.toString() + " ");
}
cursor.nextWord();
}
return sb.toString();
}
public List<String> getParagraphWords(int paragraphIndex) {
final ArrayList<String> words = new ArrayList<String>();
final ZLTextWordCursor cursor = new ZLTextWordCursor(getReader().getTextView().getStartCursor());
cursor.moveToParagraph(paragraphIndex);
cursor.moveToParagraphStart();
while (!cursor.isEndOfParagraph()) {
ZLTextElement element = cursor.getElement();
if (element instanceof ZLTextWord) {
words.add(element.toString());
}
cursor.nextWord();
}
return words;
}
public ArrayList<Integer> getParagraphWordIndices(int paragraphIndex) {
final ArrayList<Integer> indices = new ArrayList<Integer>();
final ZLTextWordCursor cursor = new ZLTextWordCursor(getReader().getTextView().getStartCursor());
cursor.moveToParagraph(paragraphIndex);
cursor.moveToParagraphStart();
while (!cursor.isEndOfParagraph()) {
ZLTextElement element = cursor.getElement();
if (element instanceof ZLTextWord) {
indices.add(cursor.getElementIndex());
}
cursor.nextWord();
}
return indices;
}
// action control
public List<String> listActions() {
// TODO: implement
return Collections.emptyList();
}
public List<String> listActionNames(List<String> actions) {
// TODO: implement
return Collections.emptyList();
}
public String getKeyAction(int key, boolean longPress) {
return myBindings.getBinding(key, longPress);
}
public void setKeyAction(int key, boolean longPress, String action) {
// TODO: implement
}
public List<String> listZoneMaps() {
return TapZoneMap.zoneMapNames();
}
public String getZoneMap() {
return getReader().PageTurningOptions.TapZoneMap.getValue();
}
public void setZoneMap(String name) {
getReader().PageTurningOptions.TapZoneMap.setValue(name);
}
public int getZoneMapHeight(String name) {
return TapZoneMap.zoneMap(name).getHeight();
}
public int getZoneMapWidth(String name) {
return TapZoneMap.zoneMap(name).getWidth();
}
public void createZoneMap(String name, int width, int height) {
TapZoneMap.createZoneMap(name, width, height);
}
public boolean isZoneMapCustom(String name) throws ApiException {
return TapZoneMap.zoneMap(name).isCustom();
}
public void deleteZoneMap(String name) throws ApiException {
TapZoneMap.deleteZoneMap(name);
}
public String getTapZoneAction(String name, int h, int v, boolean singleTap) {
return TapZoneMap.zoneMap(name).getActionByZone(
h, v, singleTap ? TapZoneMap.Tap.singleNotDoubleTap : TapZoneMap.Tap.doubleTap
);
}
public String getTapActionByCoordinates(String name, int x, int y, int width, int height, String tap) {
TapZoneMap.Tap id;
try {
id = TapZoneMap.Tap.valueOf(tap);
} catch (Exception e) {
id = TapZoneMap.Tap.singleTap;
}
return TapZoneMap.zoneMap(name).getActionByCoordinates(x, y, width, height, id);
}
public void setTapZoneAction(String name, int h, int v, boolean singleTap, String action) {
TapZoneMap.zoneMap(name).setActionForZone(h, v, singleTap, action);
}
private void setMenuTitles(List<MenuNode> nodes, ZLResource menuResource) {
for (MenuNode n : nodes) {
n.OptionalTitle = menuResource.getResource(n.Code).getValue();
if (n instanceof MenuNode.Submenu) {
setMenuTitles(((MenuNode.Submenu)n).Children, menuResource);
}
}
}
public List<MenuNode> getMainMenuContent() {
final List<MenuNode> nodes = MenuData.topLevelNodes();
final List<MenuNode> copies = new ArrayList<MenuNode>(nodes.size());
for (MenuNode n : nodes) {
copies.add(n.clone());
}
setMenuTitles(copies, ZLResource.resource("menu"));
return copies;
}
public String getResourceString(String ... keys) {
ZLResource resource = ZLResource.resource(keys[0]);
for (int i = 1; i < keys.length; ++i) {
resource = resource.getResource(keys[i]);
}
return resource.getValue();
}
public Bitmap getBitmap(int resourceId) {
return BitmapFactory.decodeResource(myContext.getResources(), resourceId);
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.api;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class ApiService extends Service {
@Override
public IBinder onBind(Intent intent) {
return new ApiServerImplementation(this);
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.api;
import android.content.Intent;
import org.geometerplus.fbreader.book.*;
public abstract class FBReaderIntents {
public static final String DEFAULT_PACKAGE = "org.geometerplus.zlibrary.ui.android";
public interface Action {
String API = "android.fbreader.action.API";
String API_CALLBACK = "android.fbreader.action.API_CALLBACK";
String VIEW = "android.fbreader.action.VIEW";
String CANCEL_MENU = "android.fbreader.action.CANCEL_MENU";
String CONFIG_SERVICE = "android.fbreader.action.CONFIG_SERVICE";
String LIBRARY_SERVICE = "android.fbreader.action.LIBRARY_SERVICE";
String BOOK_INFO = "android.fbreader.action.BOOK_INFO";
String LIBRARY = "android.fbreader.action.LIBRARY";
String EXTERNAL_LIBRARY = "android.fbreader.action.EXTERNAL_LIBRARY";
String BOOKMARKS = "android.fbreader.action.BOOKMARKS";
String EXTERNAL_BOOKMARKS = "android.fbreader.action.EXTERNAL_BOOKMARKS";
String PREFERENCES = "android.fbreader.action.PREFERENCES";
String NETWORK_LIBRARY = "android.fbreader.action.NETWORK_LIBRARY";
String OPEN_NETWORK_CATALOG = "android.fbreader.action.OPEN_NETWORK_CATALOG";
String ERROR = "android.fbreader.action.ERROR";
String CRASH = "android.fbreader.action.CRASH";
String PLUGIN = "android.fbreader.action.PLUGIN";
String CLOSE = "android.fbreader.action.CLOSE";
String PLUGIN_CRASH = "android.fbreader.action.PLUGIN_CRASH";
String EDIT_STYLES = "android.fbreader.action.EDIT_STYLES";
String EDIT_BOOKMARK = "android.fbreader.action.EDIT_BOOKMARK";
String SWITCH_YOTA_SCREEN = "android.fbreader.action.SWITCH_YOTA_SCREEN";
String SYNC_START = "android.fbreader.action.sync.START";
String SYNC_STOP = "android.fbreader.action.sync.STOP";
String SYNC_SYNC = "android.fbreader.action.sync.SYNC";
String SYNC_QUICK_SYNC = "android.fbreader.action.sync.QUICK_SYNC";
String PLUGIN_VIEW = "android.fbreader.action.plugin.VIEW";
String PLUGIN_KILL = "android.fbreader.action.plugin.KILL";
String PLUGIN_CONNECT_COVER_SERVICE = "android.fbreader.action.plugin.CONNECT_COVER_SERVICE";
}
public interface Event {
String CONFIG_OPTION_CHANGE = "fbreader.config_service.option_change_event";
String LIBRARY_BOOK = "fbreader.library_service.book_event";
String LIBRARY_BUILD = "fbreader.library_service.build_event";
String LIBRARY_COVER_READY = "fbreader.library_service.cover_ready";
String SYNC_UPDATED = "android.fbreader.event.sync.UPDATED";
}
public interface Key {
String BOOK = "fbreader.book";
String BOOKMARK = "fbreader.bookmark";
String PLUGIN = "fbreader.plugin";
String TYPE = "fbreader.type";
}
public static Intent defaultInternalIntent(String action) {
return internalIntent(action).addCategory(Intent.CATEGORY_DEFAULT);
}
public static Intent internalIntent(String action) {
return new Intent(action).setPackage(DEFAULT_PACKAGE);
}
public static void putBookExtra(Intent intent, String key, Book book) {
intent.putExtra(key, SerializerUtil.serialize(book));
}
public static void putBookExtra(Intent intent, Book book) {
putBookExtra(intent, Key.BOOK, book);
}
public static <B extends AbstractBook> B getBookExtra(Intent intent, String key, AbstractSerializer.BookCreator<B> creator) {
return SerializerUtil.deserializeBook(intent.getStringExtra(key), creator);
}
public static <B extends AbstractBook> B getBookExtra(Intent intent, AbstractSerializer.BookCreator<B> creator) {
return getBookExtra(intent, Key.BOOK, creator);
}
public static void putBookmarkExtra(Intent intent, String key, Bookmark bookmark) {
intent.putExtra(key, SerializerUtil.serialize(bookmark));
}
public static void putBookmarkExtra(Intent intent, Bookmark bookmark) {
putBookmarkExtra(intent, Key.BOOKMARK, bookmark);
}
public static Bookmark getBookmarkExtra(Intent intent, String key) {
return SerializerUtil.deserializeBookmark(intent.getStringExtra(key));
}
public static Bookmark getBookmarkExtra(Intent intent) {
return getBookmarkExtra(intent, Key.BOOKMARK);
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.api;
import java.io.Serializable;
import java.util.ArrayList;
public abstract class MenuNode implements Cloneable, Serializable {
public static final long serialVersionUID = 42L;
public final String Code;
public String OptionalTitle;
private MenuNode(String code) {
Code = code;
}
public abstract MenuNode clone();
public static final class Item extends MenuNode {
public static final long serialVersionUID = 43L;
public final Integer IconId;
public Item(String code, Integer iconId) {
super(code);
IconId = iconId;
}
public Item(String code) {
this(code, null);
}
public Item clone() {
return new Item(Code, IconId);
}
}
public static class Submenu extends MenuNode {
public static final long serialVersionUID = 44L;
public final ArrayList<MenuNode> Children = new ArrayList<MenuNode>();
public Submenu(String code) {
super(code);
}
public Submenu clone() {
final Submenu copy = new Submenu(Code);
for (MenuNode node : Children) {
copy.Children.add(node.clone());
}
return copy;
}
}
}

View file

@ -0,0 +1,113 @@
/*
* This code is in the public domain.
*/
package org.geometerplus.android.fbreader.api;
import java.util.*;
import android.content.*;
import android.net.Uri;
import android.os.*;
public abstract class PluginApi {
public static final String ACTION_REGISTER = "android.fbreader.action.plugin.REGISTER";
public static final String ACTION_RUN = "android.fbreader.action.plugin.RUN";
public static abstract class PluginInfo extends BroadcastReceiver {
public static final String KEY = "actions";
public void onReceive(Context context, Intent intent) {
final List<ActionInfo> newActions = implementedActions(context);
if (newActions != null) {
final Bundle bundle = getResultExtras(true);
ArrayList<ActionInfo> actions = bundle.<ActionInfo>getParcelableArrayList(KEY);
if (actions == null) {
actions = new ArrayList<ActionInfo>();
}
actions.addAll(newActions);
bundle.putParcelableArrayList(KEY, actions);
}
}
protected abstract List<ActionInfo> implementedActions(Context context);
}
public static abstract class ActionInfo implements Parcelable {
protected static final int TYPE_MENU_OBSOLETE = 1;
protected static final int TYPE_MENU = 2;
private final String myId;
protected ActionInfo(Uri id) {
myId = id.toString();
}
protected abstract int getType();
public Uri getId() {
return Uri.parse(myId);
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(getType());
parcel.writeString(myId);
}
public static final Creator<ActionInfo> CREATOR = new Creator<ActionInfo>() {
public ActionInfo createFromParcel(Parcel parcel) {
switch (parcel.readInt()) {
case TYPE_MENU_OBSOLETE:
return new MenuActionInfo(
Uri.parse(parcel.readString()),
parcel.readString(),
Integer.MAX_VALUE
);
case TYPE_MENU:
return new MenuActionInfo(
Uri.parse(parcel.readString()),
parcel.readString(),
parcel.readInt()
);
default:
return null;
}
}
public ActionInfo[] newArray(int size) {
return new ActionInfo[size];
}
};
}
public static class MenuActionInfo extends ActionInfo implements Comparable<MenuActionInfo> {
public final String MenuItemName;
public final int Weight;
public MenuActionInfo(Uri id, String menuItemName, int weight) {
super(id);
MenuItemName = menuItemName;
Weight = weight;
}
@Override
protected int getType() {
return TYPE_MENU;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeString(MenuItemName);
parcel.writeInt(Weight);
}
public int compareTo(MenuActionInfo info) {
return Weight - info.Weight;
}
}
}

View file

@ -0,0 +1,7 @@
/*
* This code is in the public domain.
*/
package org.geometerplus.android.fbreader.api;
parcelable TextPosition;

View file

@ -0,0 +1,45 @@
/*
* This code is in the public domain.
*/
package org.geometerplus.android.fbreader.api;
import android.os.Parcel;
import android.os.Parcelable;
public final class TextPosition extends ApiObject {
public final int ParagraphIndex;
public final int ElementIndex;
public final int CharIndex;
public TextPosition(int paragraphIndex, int elementIndex, int charIndex) {
ParagraphIndex = paragraphIndex;
ElementIndex = elementIndex;
CharIndex = charIndex;
}
@Override
protected int type() {
return Type.TEXT_POSITION;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
parcel.writeInt(ParagraphIndex);
parcel.writeInt(ElementIndex);
parcel.writeInt(CharIndex);
}
public static final Parcelable.Creator<TextPosition> CREATOR =
new Parcelable.Creator<TextPosition>() {
public TextPosition createFromParcel(Parcel parcel) {
parcel.readInt();
return new TextPosition(parcel.readInt(), parcel.readInt(), parcel.readInt());
}
public TextPosition[] newArray(int size) {
return new TextPosition[size];
}
};
}

View file

@ -0,0 +1,496 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.bookmark;
import java.util.*;
import android.app.*;
import android.os.*;
import android.view.*;
import android.widget.*;
import android.content.*;
import yuku.ambilwarna.widget.AmbilWarnaPrefWidgetView;
import org.geometerplus.zlibrary.core.util.MiscUtil;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.options.ZLStringOption;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.fbreader.book.*;
import org.geometerplus.android.fbreader.FBReader;
import org.geometerplus.android.fbreader.OrientationUtil;
import org.geometerplus.android.fbreader.api.FBReaderIntents;
import org.geometerplus.android.fbreader.libraryService.BookCollectionShadow;
import org.geometerplus.android.util.*;
public class BookmarksActivity extends Activity implements IBookCollection.Listener<Book> {
private static final int OPEN_ITEM_ID = 0;
private static final int EDIT_ITEM_ID = 1;
private static final int DELETE_ITEM_ID = 2;
private TabHost myTabHost;
private final Map<Integer,HighlightingStyle> myStyles =
Collections.synchronizedMap(new HashMap<Integer,HighlightingStyle>());
private final BookCollectionShadow myCollection = new BookCollectionShadow();
private volatile Book myBook;
private volatile Bookmark myBookmark;
private final Comparator<Bookmark> myComparator = new Bookmark.ByTimeComparator();
private volatile BookmarksAdapter myThisBookAdapter;
private volatile BookmarksAdapter myAllBooksAdapter;
private volatile BookmarksAdapter mySearchResultsAdapter;
private final ZLResource myResource = ZLResource.resource("bookmarksView");
private final ZLStringOption myBookmarkSearchPatternOption =
new ZLStringOption("BookmarkSearch", "Pattern", "");
private void createTab(String tag, int id) {
final String label = myResource.getResource(tag).getValue();
myTabHost.addTab(myTabHost.newTabSpec(tag).setIndicator(label).setContent(id));
}
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
Thread.setDefaultUncaughtExceptionHandler(new org.geometerplus.zlibrary.ui.android.library.UncaughtExceptionHandler(this));
setContentView(R.layout.bookmarks);
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
final SearchManager manager = (SearchManager)getSystemService(SEARCH_SERVICE);
manager.setOnCancelListener(null);
myTabHost = (TabHost)findViewById(R.id.bookmarks_tabhost);
myTabHost.setup();
createTab("thisBook", R.id.bookmarks_this_book);
createTab("allBooks", R.id.bookmarks_all_books);
createTab("search", R.id.bookmarks_search);
myTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
public void onTabChanged(String tabId) {
if ("search".equals(tabId)) {
findViewById(R.id.bookmarks_search_results).setVisibility(View.GONE);
onSearchRequested();
}
}
});
myBook = FBReaderIntents.getBookExtra(getIntent(), myCollection);
if (myBook == null) {
finish();
}
myBookmark = FBReaderIntents.getBookmarkExtra(getIntent());
}
@Override
protected void onStart() {
super.onStart();
myCollection.bindToService(this, new Runnable() {
public void run() {
if (myAllBooksAdapter != null) {
return;
}
myThisBookAdapter =
new BookmarksAdapter((ListView)findViewById(R.id.bookmarks_this_book), myBookmark != null);
myAllBooksAdapter =
new BookmarksAdapter((ListView)findViewById(R.id.bookmarks_all_books), false);
myCollection.addListener(BookmarksActivity.this);
updateStyles();
loadBookmarks();
}
});
OrientationUtil.setOrientation(this, getIntent());
}
private void updateStyles() {
synchronized (myStyles) {
myStyles.clear();
for (HighlightingStyle style : myCollection.highlightingStyles()) {
myStyles.put(style.Id, style);
}
}
}
private final Object myBookmarksLock = new Object();
private void loadBookmarks() {
new Thread(new Runnable() {
public void run() {
synchronized (myBookmarksLock) {
for (BookmarkQuery query = new BookmarkQuery(myBook, 50); ; query = query.next()) {
final List<Bookmark> thisBookBookmarks = myCollection.bookmarks(query);
if (thisBookBookmarks.isEmpty()) {
break;
}
myThisBookAdapter.addAll(thisBookBookmarks);
myAllBooksAdapter.addAll(thisBookBookmarks);
}
for (BookmarkQuery query = new BookmarkQuery(50); ; query = query.next()) {
final List<Bookmark> allBookmarks = myCollection.bookmarks(query);
if (allBookmarks.isEmpty()) {
break;
}
myAllBooksAdapter.addAll(allBookmarks);
}
}
}
}).start();
}
private void updateBookmarks(final Book book) {
new Thread(new Runnable() {
public void run() {
synchronized (myBookmarksLock) {
final boolean flagThisBookTab = book.getId() == myBook.getId();
final boolean flagSearchTab = mySearchResultsAdapter != null;
final Map<String,Bookmark> oldBookmarks = new HashMap<String,Bookmark>();
if (flagThisBookTab) {
for (Bookmark b : myThisBookAdapter.bookmarks()) {
oldBookmarks.put(b.Uid, b);
}
} else {
for (Bookmark b : myAllBooksAdapter.bookmarks()) {
if (b.BookId == book.getId()) {
oldBookmarks.put(b.Uid, b);
}
}
}
final String pattern = myBookmarkSearchPatternOption.getValue().toLowerCase();
for (BookmarkQuery query = new BookmarkQuery(book, 50); ; query = query.next()) {
final List<Bookmark> loaded = myCollection.bookmarks(query);
if (loaded.isEmpty()) {
break;
}
for (Bookmark b : loaded) {
final Bookmark old = oldBookmarks.remove(b.Uid);
myAllBooksAdapter.replace(old, b);
if (flagThisBookTab) {
myThisBookAdapter.replace(old, b);
}
if (flagSearchTab && MiscUtil.matchesIgnoreCase(b.getText(), pattern)) {
mySearchResultsAdapter.replace(old, b);
}
}
}
myAllBooksAdapter.removeAll(oldBookmarks.values());
if (flagThisBookTab) {
myThisBookAdapter.removeAll(oldBookmarks.values());
}
if (flagSearchTab) {
mySearchResultsAdapter.removeAll(oldBookmarks.values());
}
}
}
}).start();
}
@Override
protected void onNewIntent(Intent intent) {
OrientationUtil.setOrientation(this, intent);
if (!Intent.ACTION_SEARCH.equals(intent.getAction())) {
return;
}
String pattern = intent.getStringExtra(SearchManager.QUERY);
myBookmarkSearchPatternOption.setValue(pattern);
final LinkedList<Bookmark> bookmarks = new LinkedList<Bookmark>();
pattern = pattern.toLowerCase();
for (Bookmark b : myAllBooksAdapter.bookmarks()) {
if (MiscUtil.matchesIgnoreCase(b.getText(), pattern)) {
bookmarks.add(b);
}
}
if (!bookmarks.isEmpty()) {
final ListView resultsView = (ListView)findViewById(R.id.bookmarks_search_results);
resultsView.setVisibility(View.VISIBLE);
if (mySearchResultsAdapter == null) {
mySearchResultsAdapter = new BookmarksAdapter(resultsView, false);
} else {
mySearchResultsAdapter.clear();
}
mySearchResultsAdapter.addAll(bookmarks);
} else {
UIMessageUtil.showErrorMessage(this, "bookmarkNotFound");
}
}
@Override
protected void onDestroy() {
myCollection.unbind();
super.onDestroy();
}
@Override
public boolean onSearchRequested() {
if (DeviceType.Instance().hasStandardSearchDialog()) {
startSearch(myBookmarkSearchPatternOption.getValue(), true, null, false);
} else {
SearchDialogUtil.showDialog(this, BookmarksActivity.class, myBookmarkSearchPatternOption.getValue(), null);
}
return true;
}
@Override
public boolean onContextItemSelected(MenuItem item) {
final int position = ((AdapterView.AdapterContextMenuInfo)item.getMenuInfo()).position;
final String tag = myTabHost.getCurrentTabTag();
final BookmarksAdapter adapter;
if ("thisBook".equals(tag)) {
adapter = myThisBookAdapter;
} else if ("allBooks".equals(tag)) {
adapter = myAllBooksAdapter;
} else if ("search".equals(tag)) {
adapter = mySearchResultsAdapter;
} else {
throw new RuntimeException("Unknown tab tag: " + tag);
}
final Bookmark bookmark = adapter.getItem(position);
switch (item.getItemId()) {
case OPEN_ITEM_ID:
gotoBookmark(bookmark);
return true;
case EDIT_ITEM_ID:
final Intent intent = new Intent(this, EditBookmarkActivity.class);
FBReaderIntents.putBookmarkExtra(intent, bookmark);
OrientationUtil.startActivity(this, intent);
return true;
case DELETE_ITEM_ID:
myCollection.deleteBookmark(bookmark);
return true;
}
return super.onContextItemSelected(item);
}
private void gotoBookmark(Bookmark bookmark) {
bookmark.markAsAccessed();
myCollection.saveBookmark(bookmark);
final Book book = myCollection.getBookById(bookmark.BookId);
if (book != null) {
FBReader.openBookActivity(this, book, bookmark);
} else {
UIMessageUtil.showErrorMessage(this, "cannotOpenBook");
}
}
private final class BookmarksAdapter extends BaseAdapter implements AdapterView.OnItemClickListener, View.OnCreateContextMenuListener {
private final List<Bookmark> myBookmarksList =
Collections.synchronizedList(new LinkedList<Bookmark>());
private volatile boolean myShowAddBookmarkItem;
BookmarksAdapter(ListView listView, boolean showAddBookmarkItem) {
myShowAddBookmarkItem = showAddBookmarkItem;
listView.setAdapter(this);
listView.setOnItemClickListener(this);
listView.setOnCreateContextMenuListener(this);
}
public List<Bookmark> bookmarks() {
return Collections.unmodifiableList(myBookmarksList);
}
public void addAll(final List<Bookmark> bookmarks) {
runOnUiThread(new Runnable() {
public void run() {
synchronized (myBookmarksList) {
for (Bookmark b : bookmarks) {
final int position = Collections.binarySearch(myBookmarksList, b, myComparator);
if (position < 0) {
myBookmarksList.add(- position - 1, b);
}
}
}
notifyDataSetChanged();
}
});
}
private boolean areEqualsForView(Bookmark b0, Bookmark b1) {
return
b0.getStyleId() == b1.getStyleId() &&
b0.getText().equals(b1.getText()) &&
b0.getTimestamp(Bookmark.DateType.Latest).equals(b1.getTimestamp(Bookmark.DateType.Latest));
}
public void replace(final Bookmark old, final Bookmark b) {
if (old != null && areEqualsForView(old, b)) {
return;
}
runOnUiThread(new Runnable() {
public void run() {
synchronized (myBookmarksList) {
if (old != null) {
myBookmarksList.remove(old);
}
final int position = Collections.binarySearch(myBookmarksList, b, myComparator);
if (position < 0) {
myBookmarksList.add(- position - 1, b);
}
}
notifyDataSetChanged();
}
});
}
public void removeAll(final Collection<Bookmark> bookmarks) {
if (bookmarks.isEmpty()) {
return;
}
runOnUiThread(new Runnable() {
public void run() {
myBookmarksList.removeAll(bookmarks);
notifyDataSetChanged();
}
});
}
public void clear() {
runOnUiThread(new Runnable() {
public void run() {
myBookmarksList.clear();
notifyDataSetChanged();
}
});
}
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
final int position = ((AdapterView.AdapterContextMenuInfo)menuInfo).position;
if (getItem(position) != null) {
menu.add(0, OPEN_ITEM_ID, 0, myResource.getResource("openBook").getValue());
menu.add(0, EDIT_ITEM_ID, 0, myResource.getResource("editBookmark").getValue());
menu.add(0, DELETE_ITEM_ID, 0, myResource.getResource("deleteBookmark").getValue());
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final View view = (convertView != null) ? convertView :
LayoutInflater.from(parent.getContext()).inflate(R.layout.bookmark_item, parent, false);
final ImageView imageView = ViewUtil.findImageView(view, R.id.bookmark_item_icon);
final View colorContainer = ViewUtil.findView(view, R.id.bookmark_item_color_container);
final AmbilWarnaPrefWidgetView colorView =
(AmbilWarnaPrefWidgetView)ViewUtil.findView(view, R.id.bookmark_item_color);
final TextView textView = ViewUtil.findTextView(view, R.id.bookmark_item_text);
final TextView bookTitleView = ViewUtil.findTextView(view, R.id.bookmark_item_booktitle);
final Bookmark bookmark = getItem(position);
if (bookmark == null) {
imageView.setVisibility(View.VISIBLE);
imageView.setImageResource(R.drawable.ic_list_plus);
colorContainer.setVisibility(View.GONE);
textView.setText(myResource.getResource("new").getValue());
bookTitleView.setVisibility(View.GONE);
} else {
imageView.setVisibility(View.GONE);
colorContainer.setVisibility(View.VISIBLE);
BookmarksUtil.setupColorView(colorView, myStyles.get(bookmark.getStyleId()));
textView.setText(bookmark.getText());
if (myShowAddBookmarkItem) {
bookTitleView.setVisibility(View.GONE);
} else {
bookTitleView.setVisibility(View.VISIBLE);
bookTitleView.setText(bookmark.BookTitle);
}
}
return view;
}
@Override
public final boolean areAllItemsEnabled() {
return true;
}
@Override
public final boolean isEnabled(int position) {
return true;
}
@Override
public final long getItemId(int position) {
final Bookmark item = getItem(position);
return item != null ? item.getId() : -1;
}
@Override
public final Bookmark getItem(int position) {
if (myShowAddBookmarkItem) {
--position;
}
return position >= 0 ? myBookmarksList.get(position) : null;
}
@Override
public final int getCount() {
return myShowAddBookmarkItem ? myBookmarksList.size() + 1 : myBookmarksList.size();
}
public final void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final Bookmark bookmark = getItem(position);
if (bookmark != null) {
gotoBookmark(bookmark);
} else if (myShowAddBookmarkItem) {
myShowAddBookmarkItem = false;
myCollection.saveBookmark(myBookmark);
}
}
}
// method from IBookCollection.Listener
public void onBookEvent(BookEvent event, Book book) {
switch (event) {
default:
break;
case BookmarkStyleChanged:
runOnUiThread(new Runnable() {
public void run() {
updateStyles();
myAllBooksAdapter.notifyDataSetChanged();
myThisBookAdapter.notifyDataSetChanged();
if (mySearchResultsAdapter != null) {
mySearchResultsAdapter.notifyDataSetChanged();
}
}
});
break;
case BookmarksUpdated:
updateBookmarks(book);
break;
}
}
// method from IBookCollection.Listener
public void onBuildEvent(IBookCollection.Status status) {
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.bookmark;
import yuku.ambilwarna.widget.AmbilWarnaPrefWidgetView;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.ui.android.util.ZLAndroidColorUtil;
import org.geometerplus.fbreader.book.HighlightingStyle;
abstract class BookmarksUtil {
static void setupColorView(AmbilWarnaPrefWidgetView colorView, HighlightingStyle style) {
Integer rgb = null;
if (style != null) {
final ZLColor color = style.getBackgroundColor();
if (color != null) {
rgb = ZLAndroidColorUtil.rgb(color);
}
}
if (rgb != null) {
colorView.showCross(false);
colorView.setBackgroundColor(rgb);
} else {
colorView.showCross(true);
colorView.setBackgroundColor(0);
}
}
}

View file

@ -0,0 +1,267 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.bookmark;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.*;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.*;
import android.widget.*;
import yuku.ambilwarna.widget.AmbilWarnaPrefWidgetView;
import org.geometerplus.zlibrary.core.options.ZLStringOption;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.zlibrary.ui.android.util.ZLAndroidColorUtil;
import org.geometerplus.fbreader.book.*;
import org.geometerplus.android.fbreader.OrientationUtil;
import org.geometerplus.android.fbreader.api.FBReaderIntents;
import org.geometerplus.android.fbreader.libraryService.BookCollectionShadow;
import org.geometerplus.android.util.ViewUtil;
public class EditBookmarkActivity extends Activity implements IBookCollection.Listener<Book> {
private final ZLResource myResource = ZLResource.resource("editBookmark");
private final BookCollectionShadow myCollection = new BookCollectionShadow();
private Bookmark myBookmark;
private StyleListAdapter myStylesAdapter;
private void addTab(TabHost host, String id, int content) {
final TabHost.TabSpec spec = host.newTabSpec(id);
spec.setIndicator(myResource.getResource(id).getValue());
spec.setContent(content);
host.addTab(spec);
}
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.edit_bookmark);
myBookmark = FBReaderIntents.getBookmarkExtra(getIntent());
if (myBookmark == null) {
finish();
return;
}
final DisplayMetrics dm = getResources().getDisplayMetrics();
final int width = Math.min(
(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 500, dm),
dm.widthPixels * 9 / 10
);
final int height = Math.min(
(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 350, dm),
dm.heightPixels * 9 / 10
);
final TabHost tabHost = (TabHost)findViewById(R.id.edit_bookmark_tabhost);
tabHost.setLayoutParams(new FrameLayout.LayoutParams(
new ViewGroup.LayoutParams(width, height)
));
tabHost.setup();
addTab(tabHost, "text", R.id.edit_bookmark_content_text);
addTab(tabHost, "style", R.id.edit_bookmark_content_style);
addTab(tabHost, "delete", R.id.edit_bookmark_content_delete);
final ZLStringOption currentTabOption =
new ZLStringOption("LookNFeel", "EditBookmark", "text");
tabHost.setCurrentTabByTag(currentTabOption.getValue());
tabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
public void onTabChanged(String tag) {
if (!"delete".equals(tag)) {
currentTabOption.setValue(tag);
}
}
});
final EditText editor = (EditText)findViewById(R.id.edit_bookmark_text);
editor.setText(myBookmark.getText());
final int len = editor.getText().length();
editor.setSelection(len, len);
final Button saveTextButton = (Button)findViewById(R.id.edit_bookmark_save_text_button);
saveTextButton.setEnabled(false);
saveTextButton.setText(myResource.getResource("saveText").getValue());
saveTextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
myCollection.bindToService(EditBookmarkActivity.this, new Runnable() {
public void run() {
myBookmark.setText(editor.getText().toString());
myCollection.saveBookmark(myBookmark);
saveTextButton.setEnabled(false);
}
});
}
});
editor.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence sequence, int start, int before, int count) {
final String originalText = myBookmark.getText();
saveTextButton.setEnabled(!originalText.equals(editor.getText().toString()));
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
final Button deleteButton = (Button)findViewById(R.id.edit_bookmark_delete_button);
deleteButton.setText(myResource.getResource("deleteBookmark").getValue());
deleteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
myCollection.bindToService(EditBookmarkActivity.this, new Runnable() {
public void run() {
myCollection.deleteBookmark(myBookmark);
finish();
}
});
}
});
}
@Override
protected void onStart() {
super.onStart();
myCollection.bindToService(this, new Runnable() {
public void run() {
final List<HighlightingStyle> styles = myCollection.highlightingStyles();
if (styles.isEmpty()) {
finish();
return;
}
myStylesAdapter = new StyleListAdapter(styles);
final ListView stylesList =
(ListView)findViewById(R.id.edit_bookmark_content_style);
stylesList.setAdapter(myStylesAdapter);
stylesList.setOnItemClickListener(myStylesAdapter);
myCollection.addListener(EditBookmarkActivity.this);
}
});
}
@Override
protected void onDestroy() {
myCollection.unbind();
super.onDestroy();
}
// method from IBookCollection.Listener
public void onBookEvent(BookEvent event, Book book) {
if (event == BookEvent.BookmarkStyleChanged) {
myStylesAdapter.setStyleList(myCollection.highlightingStyles());
}
}
// method from IBookCollection.Listener
public void onBuildEvent(IBookCollection.Status status) {
}
private class StyleListAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
private final List<HighlightingStyle> myStyles;
StyleListAdapter(List<HighlightingStyle> styles) {
myStyles = new ArrayList<HighlightingStyle>(styles);
}
public synchronized void setStyleList(List<HighlightingStyle> styles) {
myStyles.clear();
myStyles.addAll(styles);
notifyDataSetChanged();
}
public final synchronized int getCount() {
return myStyles.size();
}
public final synchronized HighlightingStyle getItem(int position) {
return myStyles.get(position);
}
public final long getItemId(int position) {
return position;
}
public final synchronized View getView(int position, View convertView, final ViewGroup parent) {
final View view = convertView != null
? convertView
: LayoutInflater.from(parent.getContext()).inflate(R.layout.style_item, parent, false);
final HighlightingStyle style = getItem(position);
final CheckBox checkBox = (CheckBox)ViewUtil.findView(view, R.id.style_item_checkbox);
final AmbilWarnaPrefWidgetView colorView =
(AmbilWarnaPrefWidgetView)ViewUtil.findView(view, R.id.style_item_color);
final TextView titleView = ViewUtil.findTextView(view, R.id.style_item_title);
final Button button = (Button)ViewUtil.findView(view, R.id.style_item_edit_button);
checkBox.setChecked(style.Id == myBookmark.getStyleId());
colorView.setVisibility(View.VISIBLE);
BookmarksUtil.setupColorView(colorView, style);
titleView.setText(BookmarkUtil.getStyleName(style));
button.setVisibility(View.VISIBLE);
button.setText(myResource.getResource("editStyle").getValue());
button.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(
new Intent(EditBookmarkActivity.this, EditStyleActivity.class)
.putExtra(EditStyleActivity.STYLE_ID_KEY, style.Id)
);
}
});
return view;
}
public final synchronized void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final HighlightingStyle style = getItem(position);
myCollection.bindToService(EditBookmarkActivity.this, new Runnable() {
public void run() {
myBookmark.setStyleId(style.Id);
myCollection.setDefaultHighlightingStyleId(style.Id);
myCollection.saveBookmark(myBookmark);
}
});
notifyDataSetChanged();
}
}
}

View file

@ -0,0 +1,138 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.bookmark;
import android.content.Context;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.view.Window;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.fbreader.book.BookmarkUtil;
import org.geometerplus.fbreader.book.HighlightingStyle;
import org.geometerplus.android.fbreader.libraryService.BookCollectionShadow;
import org.geometerplus.android.fbreader.preferences.*;
public class EditStyleActivity extends PreferenceActivity {
static final String STYLE_ID_KEY = "style.id";
private final ZLResource myRootResource = ZLResource.resource("editStyle");
private final BookCollectionShadow myCollection = new BookCollectionShadow();
private HighlightingStyle myStyle;
private BgColorPreference myBgColorPreference;
@Override
protected void onCreate(Bundle bundle) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(bundle);
Thread.setDefaultUncaughtExceptionHandler(new org.geometerplus.zlibrary.ui.android.library.UncaughtExceptionHandler(this));
final PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(this);
setPreferenceScreen(screen);
myCollection.bindToService(this, new Runnable() {
public void run() {
myStyle = myCollection.getHighlightingStyle(getIntent().getIntExtra(STYLE_ID_KEY, -1));
if (myStyle == null) {
finish();
return;
}
screen.addPreference(new NamePreference());
screen.addPreference(new InvisiblePreference());
myBgColorPreference = new BgColorPreference();
screen.addPreference(myBgColorPreference);
}
});
}
@Override
protected void onDestroy() {
myCollection.unbind();
super.onDestroy();
}
private class NamePreference extends ZLStringPreference {
NamePreference() {
super(EditStyleActivity.this, myRootResource, "name");
super.setValue(BookmarkUtil.getStyleName(myStyle));
}
@Override
protected void setValue(String value) {
super.setValue(value);
BookmarkUtil.setStyleName(myStyle, value);
myCollection.saveHighlightingStyle(myStyle);
}
}
private class InvisiblePreference extends ZLCheckBoxPreference {
private ZLColor mySavedBgColor;
InvisiblePreference() {
super(EditStyleActivity.this, myRootResource.getResource("invisible"));
setChecked(myStyle.getBackgroundColor() == null);
}
@Override
protected void onClick() {
super.onClick();
if (isChecked()) {
mySavedBgColor = myStyle.getBackgroundColor();
myStyle.setBackgroundColor(null);
myBgColorPreference.setEnabled(false);
} else {
myStyle.setBackgroundColor(
mySavedBgColor != null ? mySavedBgColor : new ZLColor(127, 127, 127)
);
myBgColorPreference.setEnabled(true);
}
myCollection.saveHighlightingStyle(myStyle);
}
}
private class BgColorPreference extends ColorPreference {
BgColorPreference() {
super(EditStyleActivity.this);
setEnabled(getSavedColor() != null);
}
@Override
public String getTitle() {
return myRootResource.getResource("bgColor").getValue();
}
@Override
protected ZLColor getSavedColor() {
return myStyle.getBackgroundColor();
}
@Override
protected void saveColor(ZLColor color) {
myStyle.setBackgroundColor(color);
myCollection.saveHighlightingStyle(myStyle);
}
}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.config;
import java.util.List;
interface ConfigInterface {
List<String> listGroups();
List<String> listNames(in String group);
String getValue(in String group, in String name);
void setValue(in String group, in String name, in String value);
void unsetValue(in String group, in String name);
void removeGroup(in String name);
List<String> requestAllValuesForGroup(in String group);
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.config;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class ConfigService extends Service {
private ConfigInterface.Stub myConfig;
@Override
public IBinder onBind(Intent intent) {
return myConfig;
}
@Override
public void onCreate() {
super.onCreate();
myConfig = new SQLiteConfig(this);
}
@Override
public void onDestroy() {
if (myConfig != null) {
// TODO: close db
myConfig = null;
}
super.onDestroy();
}
}

View file

@ -0,0 +1,210 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.config;
import java.util.*;
import android.app.Service;
import android.content.*;
import android.os.IBinder;
import android.os.RemoteException;
import org.geometerplus.zlibrary.core.options.Config;
import org.geometerplus.android.fbreader.api.FBReaderIntents;
public final class ConfigShadow extends Config implements ServiceConnection {
private final Context myContext;
private volatile ConfigInterface myInterface;
private final List<Runnable> myDeferredActions = new LinkedList<Runnable>();
private final BroadcastReceiver myReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
try {
setToCache(
intent.getStringExtra("group"),
intent.getStringExtra("name"),
intent.getStringExtra("value")
);
} catch (Exception e) {
// ignore
}
}
};
public ConfigShadow(Context context) {
myContext = context;
context.bindService(
FBReaderIntents.internalIntent(FBReaderIntents.Action.CONFIG_SERVICE),
this,
Service.BIND_AUTO_CREATE
);
}
@Override
public boolean isInitialized() {
return myInterface != null;
}
@Override
public void runOnConnect(Runnable runnable) {
if (myInterface != null) {
runnable.run();
} else {
synchronized (myDeferredActions) {
myDeferredActions.add(runnable);
}
}
}
@Override
public List<String> listGroups() {
if (myInterface == null) {
return Collections.emptyList();
}
try {
return myInterface.listGroups();
} catch (RemoteException e) {
return Collections.emptyList();
}
}
@Override
public List<String> listNames(String group) {
if (myInterface == null) {
return Collections.emptyList();
}
try {
return myInterface.listNames(group);
} catch (RemoteException e) {
return Collections.emptyList();
}
}
@Override
public void removeGroup(String name) {
if (myInterface != null) {
try {
myInterface.removeGroup(name);
} catch (RemoteException e) {
}
}
}
public boolean getSpecialBooleanValue(String name, boolean defaultValue) {
return myContext.getSharedPreferences("fbreader.ui", Context.MODE_PRIVATE)
.getBoolean(name, defaultValue);
}
public void setSpecialBooleanValue(String name, boolean value) {
myContext.getSharedPreferences("fbreader.ui", Context.MODE_PRIVATE).edit()
.putBoolean(name, value).commit();
}
public String getSpecialStringValue(String name, String defaultValue) {
return myContext.getSharedPreferences("fbreader.ui", Context.MODE_PRIVATE)
.getString(name, defaultValue);
}
public void setSpecialStringValue(String name, String value) {
myContext.getSharedPreferences("fbreader.ui", Context.MODE_PRIVATE).edit()
.putString(name, value).commit();
}
@Override
protected String getValueInternal(String group, String name) throws NotAvailableException {
if (myInterface == null) {
throw new NotAvailableException("Config is not initialized for " + group + ":" + name);
}
try {
return myInterface.getValue(group, name);
} catch (RemoteException e) {
throw new NotAvailableException("RemoteException for " + group + ":" + name);
}
}
@Override
protected void setValueInternal(String group, String name, String value) {
if (myInterface != null) {
try {
myInterface.setValue(group, name, value);
} catch (RemoteException e) {
}
}
}
@Override
protected void unsetValueInternal(String group, String name) {
if (myInterface != null) {
try {
myInterface.unsetValue(group, name);
} catch (RemoteException e) {
}
}
}
@Override
protected Map<String,String> requestAllValuesForGroupInternal(String group) throws NotAvailableException {
if (myInterface == null) {
throw new NotAvailableException("Config is not initialized for " + group);
}
try {
final Map<String,String> values = new HashMap<String,String>();
for (String pair : myInterface.requestAllValuesForGroup(group)) {
final String[] split = pair.split("\000");
switch (split.length) {
case 1:
values.put(split[0], "");
break;
case 2:
values.put(split[0], split[1]);
break;
}
}
return values;
} catch (RemoteException e) {
throw new NotAvailableException("RemoteException for " + group);
}
}
// method from ServiceConnection interface
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (this) {
myInterface = ConfigInterface.Stub.asInterface(service);
myContext.registerReceiver(
myReceiver, new IntentFilter(FBReaderIntents.Event.CONFIG_OPTION_CHANGE)
);
}
final List<Runnable> actions;
synchronized (myDeferredActions) {
actions = new ArrayList<Runnable>(myDeferredActions);
myDeferredActions.clear();
}
for (Runnable a : actions) {
a.run();
}
}
// method from ServiceConnection interface
public synchronized void onServiceDisconnected(ComponentName name) {
myContext.unregisterReceiver(myReceiver);
}
}

View file

@ -0,0 +1,172 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.config;
import java.util.*;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import org.geometerplus.android.fbreader.api.FBReaderIntents;
final class SQLiteConfig extends ConfigInterface.Stub {
private final Service myService;
private final SQLiteDatabase myDatabase;
private final SQLiteStatement myGetValueStatement;
private final SQLiteStatement mySetValueStatement;
private final SQLiteStatement myUnsetValueStatement;
private final SQLiteStatement myDeleteGroupStatement;
public SQLiteConfig(Service service) {
myService = service;
myDatabase = service.openOrCreateDatabase("config.db", Context.MODE_PRIVATE, null);
switch (myDatabase.getVersion()) {
case 0:
myDatabase.execSQL("CREATE TABLE IF NOT EXISTS config (groupName VARCHAR, name VARCHAR, value VARCHAR, PRIMARY KEY(groupName, name) )");
break;
case 1:
myDatabase.beginTransaction();
SQLiteStatement removeStatement = myDatabase.compileStatement(
"DELETE FROM config WHERE name = ? AND groupName LIKE ?"
);
removeStatement.bindString(2, "/%");
removeStatement.bindString(1, "Size"); removeStatement.execute();
removeStatement.bindString(1, "Title"); removeStatement.execute();
removeStatement.bindString(1, "Language"); removeStatement.execute();
removeStatement.bindString(1, "Encoding"); removeStatement.execute();
removeStatement.bindString(1, "AuthorSortKey"); removeStatement.execute();
removeStatement.bindString(1, "AuthorDisplayName"); removeStatement.execute();
removeStatement.bindString(1, "EntriesNumber"); removeStatement.execute();
removeStatement.bindString(1, "TagList"); removeStatement.execute();
removeStatement.bindString(1, "Sequence"); removeStatement.execute();
removeStatement.bindString(1, "Number in seq"); removeStatement.execute();
myDatabase.execSQL(
"DELETE FROM config WHERE name LIKE 'Entry%' AND groupName LIKE '/%'"
);
myDatabase.setTransactionSuccessful();
myDatabase.endTransaction();
myDatabase.execSQL("VACUUM");
break;
}
myDatabase.setVersion(2);
myGetValueStatement = myDatabase.compileStatement("SELECT value FROM config WHERE groupName = ? AND name = ?");
mySetValueStatement = myDatabase.compileStatement("INSERT OR REPLACE INTO config (groupName, name, value) VALUES (?, ?, ?)");
myUnsetValueStatement = myDatabase.compileStatement("DELETE FROM config WHERE groupName = ? AND name = ?");
myDeleteGroupStatement = myDatabase.compileStatement("DELETE FROM config WHERE groupName = ?");
}
@Override
synchronized public List<String> listGroups() {
final LinkedList<String> list = new LinkedList<String>();
final Cursor cursor = myDatabase.rawQuery("SELECT DISTINCT groupName FROM config", null);
while (cursor.moveToNext()) {
list.add(cursor.getString(0));
}
cursor.close();
return list;
}
@Override
synchronized public List<String> listNames(String group) {
final LinkedList<String> list = new LinkedList<String>();
final Cursor cursor = myDatabase.rawQuery("SELECT name FROM config WHERE groupName = ?", new String[] { group });
while (cursor.moveToNext()) {
list.add(cursor.getString(0));
}
cursor.close();
return list;
}
@Override
synchronized public void removeGroup(String name) {
myDeleteGroupStatement.bindString(1, name);
try {
myDeleteGroupStatement.execute();
} catch (SQLException e) {
}
}
@Override
synchronized public List<String> requestAllValuesForGroup(String group) {
try {
final List<String> pairs = new LinkedList<String>();
final Cursor cursor = myDatabase.rawQuery(
"SELECT name,value FROM config WHERE groupName = ?",
new String[] { group }
);
while (cursor.moveToNext()) {
pairs.add(cursor.getString(0) + "\000" + cursor.getString(1));
}
cursor.close();
return pairs;
} catch (SQLException e) {
return Collections.emptyList();
}
}
@Override
synchronized public String getValue(String group, String name) {
myGetValueStatement.bindString(1, group);
myGetValueStatement.bindString(2, name);
try {
return myGetValueStatement.simpleQueryForString();
} catch (SQLException e) {
return null;
}
}
@Override
synchronized public void setValue(String group, String name, String value) {
mySetValueStatement.bindString(1, group);
mySetValueStatement.bindString(2, name);
mySetValueStatement.bindString(3, value);
try {
mySetValueStatement.execute();
sendChangeEvent(group, name, value);
} catch (SQLException e) {
}
}
@Override
synchronized public void unsetValue(String group, String name) {
myUnsetValueStatement.bindString(1, group);
myUnsetValueStatement.bindString(2, name);
try {
myUnsetValueStatement.execute();
sendChangeEvent(group, name, null);
} catch (SQLException e) {
}
}
private void sendChangeEvent(String group, String name, String value) {
myService.sendBroadcast(
new Intent(FBReaderIntents.Event.CONFIG_OPTION_CHANGE)
.putExtra("group", group)
.putExtra("name", name)
.putExtra("value", value)
);
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.covers;
import java.util.*;
import android.graphics.Bitmap;
import org.geometerplus.fbreader.tree.FBTree;
class CoverCache {
static class NullObjectException extends Exception {
}
private static final Object NULL_BITMAP = new Object();
volatile int HoldersCounter = 0;
private final Map<FBTree.Key,Object> myBitmaps =
Collections.synchronizedMap(new LinkedHashMap<FBTree.Key,Object>(10, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<FBTree.Key,Object> eldest) {
return size() > 3 * HoldersCounter;
}
});
Bitmap getBitmap(FBTree.Key key) throws NullObjectException {
final Object bitmap = myBitmaps.get(key);
if (bitmap == NULL_BITMAP) {
throw new NullObjectException();
}
return (Bitmap)bitmap;
}
void putBitmap(FBTree.Key key, Bitmap bitmap) {
myBitmaps.put(key, bitmap != null ? bitmap : NULL_BITMAP);
}
}

View file

@ -0,0 +1,167 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.covers;
import java.util.concurrent.Future;
import android.widget.ImageView;
import android.graphics.Bitmap;
import org.geometerplus.zlibrary.core.image.ZLImageProxy;
import org.geometerplus.fbreader.tree.FBTree;
class CoverHolder {
private final CoverManager myManager;
final ImageView CoverView;
volatile FBTree.Key Key;
private CoverSyncRunnable coverSyncRunnable;
Future<?> coverBitmapTask;
private Runnable coverBitmapRunnable;
CoverHolder(CoverManager manager, ImageView coverView, FBTree.Key key) {
myManager = manager;
manager.setupCoverView(coverView);
CoverView = coverView;
Key = key;
myManager.Cache.HoldersCounter++;
}
synchronized void setKey(FBTree.Key key) {
if (!Key.equals(key)) {
if (coverBitmapTask != null) {
coverBitmapTask.cancel(true);
coverBitmapTask = null;
}
coverBitmapRunnable = null;
}
Key = key;
}
class CoverSyncRunnable implements Runnable {
private final ZLImageProxy myImage;
private final FBTree.Key myKey;
CoverSyncRunnable(ZLImageProxy image) {
myImage = image;
synchronized (CoverHolder.this) {
myKey = Key;
coverSyncRunnable = this;
}
}
public void run() {
synchronized (CoverHolder.this) {
try {
if (coverSyncRunnable != this) {
return;
}
if (!Key.equals(myKey)) {
return;
}
if (!myImage.isSynchronized()) {
return;
}
myManager.runOnUiThread(new Runnable() {
@Override
public void run() {
synchronized (CoverHolder.this) {
if (Key.equals(myKey)) {
myManager.setCoverForView(CoverHolder.this, myImage);
}
}
}
});
} finally {
if (coverSyncRunnable == this) {
coverSyncRunnable = null;
}
}
}
}
}
class CoverBitmapRunnable implements Runnable {
private final ZLImageProxy myImage;
private final FBTree.Key myKey;
CoverBitmapRunnable(ZLImageProxy image) {
myImage = image;
synchronized (CoverHolder.this) {
myKey = Key;
coverBitmapRunnable = this;
}
}
public void run() {
synchronized (CoverHolder.this) {
if (coverBitmapRunnable != this) {
return;
}
}
try {
if (!myImage.isSynchronized()) {
return;
}
final Bitmap coverBitmap = myManager.getBitmap(myImage);
if (coverBitmap == null) {
// If bitmap is null, then there's no image
// and CoverView already has a stock image
myManager.Cache.putBitmap(myKey, null);
return;
}
if (Thread.currentThread().isInterrupted()) {
// We have been cancelled
return;
}
/*
synchronized (CoverHolder.this) {
// I'm not sure why, but cover bitmaps disappear all the time
// So if by the time bitmap is generated holder has switched
// to another key/tree, just scrap it, will retry later
if (!Key.equals(myKey)) {
return;
}
}
*/
myManager.Cache.putBitmap(myKey, coverBitmap);
myManager.runOnUiThread(new Runnable() {
@Override
public void run() {
synchronized (CoverHolder.this) {
if (Key.equals(myKey)) {
CoverView.setImageBitmap(coverBitmap);
}
}
}
});
} finally {
synchronized (CoverHolder.this) {
if (coverBitmapRunnable == this) {
coverBitmapRunnable = null;
coverBitmapTask = null;
}
}
}
}
}
}

View file

@ -0,0 +1,139 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.covers;
import java.util.concurrent.*;
import android.app.Activity;
import android.graphics.Bitmap;
import android.widget.ImageView;
import org.geometerplus.zlibrary.core.image.ZLImage;
import org.geometerplus.zlibrary.core.image.ZLImageProxy;
import org.geometerplus.zlibrary.ui.android.image.ZLAndroidImageData;
import org.geometerplus.zlibrary.ui.android.image.ZLAndroidImageManager;
import org.geometerplus.fbreader.tree.FBTree;
public class CoverManager {
final CoverCache Cache = new CoverCache();
private static class MinPriorityThreadFactory implements ThreadFactory {
private final ThreadFactory myDefaultThreadFactory = Executors.defaultThreadFactory();
public Thread newThread(Runnable r) {
final Thread th = myDefaultThreadFactory.newThread(r);
th.setPriority(Thread.MIN_PRIORITY);
return th;
}
}
private final ExecutorService myPool = Executors.newFixedThreadPool(1, new MinPriorityThreadFactory());
private final Activity myActivity;
private final ZLImageProxy.Synchronizer myImageSynchronizer;
private final int myCoverWidth;
private final int myCoverHeight;
public CoverManager(Activity activity, ZLImageProxy.Synchronizer synchronizer, int coverWidth, int coverHeight) {
myActivity = activity;
myImageSynchronizer = synchronizer;
myCoverWidth = coverWidth;
myCoverHeight = coverHeight;
}
void runOnUiThread(Runnable runnable) {
myActivity.runOnUiThread(runnable);
}
void setupCoverView(ImageView coverView) {
coverView.getLayoutParams().width = myCoverWidth;
coverView.getLayoutParams().height = myCoverHeight;
coverView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
coverView.requestLayout();
}
Bitmap getBitmap(ZLImage image) {
final ZLAndroidImageManager mgr = (ZLAndroidImageManager)ZLAndroidImageManager.Instance();
final ZLAndroidImageData data = mgr.getImageData(image);
if (data == null) {
return null;
}
return data.getBitmap(2 * myCoverWidth, 2 * myCoverHeight);
}
void setCoverForView(CoverHolder holder, ZLImageProxy image) {
synchronized (holder) {
try {
final Bitmap coverBitmap = Cache.getBitmap(holder.Key);
if (coverBitmap != null) {
holder.CoverView.setImageBitmap(coverBitmap);
} else if (holder.coverBitmapTask == null) {
holder.coverBitmapTask = myPool.submit(holder.new CoverBitmapRunnable(image));
}
} catch (CoverCache.NullObjectException e) {
}
}
}
private CoverHolder getHolder(ImageView coverView, FBTree tree) {
CoverHolder holder = (CoverHolder)coverView.getTag();
if (holder == null) {
holder = new CoverHolder(this, coverView, tree.getUniqueKey());
coverView.setTag(holder);
} else {
holder.setKey(tree.getUniqueKey());
}
return holder;
}
public boolean trySetCoverImage(ImageView coverView, FBTree tree) {
final CoverHolder holder = getHolder(coverView, tree);
Bitmap coverBitmap;
try {
coverBitmap = Cache.getBitmap(holder.Key);
} catch (CoverCache.NullObjectException e) {
return false;
}
if (coverBitmap == null) {
final ZLImage cover = tree.getCover();
if (cover instanceof ZLImageProxy) {
final ZLImageProxy img = (ZLImageProxy)cover;
if (img.isSynchronized()) {
setCoverForView(holder, img);
} else {
img.startSynchronization(
myImageSynchronizer,
holder.new CoverSyncRunnable(img)
);
}
} else if (cover != null) {
coverBitmap = getBitmap(cover);
}
}
if (coverBitmap != null) {
holder.CoverView.setImageBitmap(coverBitmap);
return true;
}
return false;
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.crash;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.*;
import org.geometerplus.zlibrary.core.options.Config;
import org.geometerplus.zlibrary.core.options.ZLStringOption;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.fbreader.Paths;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.android.fbreader.FBReader;
import org.geometerplus.android.util.FileChooserUtil;
public class FixBooksDirectoryActivity extends Activity {
private TextView myDirectoryView;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.books_directory_fix);
final ZLResource resource = ZLResource.resource("crash").getResource("fixBooksDirectory");
final ZLResource buttonResource = ZLResource.resource("dialog").getResource("button");
final String title = resource.getResource("title").getValue();
setTitle(title);
final TextView textView = (TextView)findViewById(R.id.books_directory_fix_text);
textView.setText(resource.getResource("text").getValue());
myDirectoryView = (TextView)findViewById(R.id.books_directory_fix_directory);
final View buttonsView = findViewById(R.id.books_directory_fix_buttons);
final Button okButton = (Button)buttonsView.findViewById(R.id.ok_button);
okButton.setText(buttonResource.getResource("ok").getValue());
final View selectButton = findViewById(R.id.books_directory_fix_select_button);
Config.Instance().runOnConnect(new Runnable() {
public void run() {
final ZLStringOption tempDirectoryOption = Paths.TempDirectoryOption(FixBooksDirectoryActivity.this);
myDirectoryView.setText(tempDirectoryOption.getValue());
selectButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FileChooserUtil.runDirectoryChooser(
FixBooksDirectoryActivity.this,
1,
title,
tempDirectoryOption.getValue(),
true
);
}
});
okButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
final String newDirectory = myDirectoryView.getText().toString();
tempDirectoryOption.setValue(newDirectory);
startActivity(new Intent(FixBooksDirectoryActivity.this, FBReader.class));
finish();
}
});
}
});
final Button cancelButton = (Button)buttonsView.findViewById(R.id.cancel_button);
cancelButton.setText(buttonResource.getResource("cancel").getValue());
cancelButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
finish();
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1 && resultCode == RESULT_OK) {
myDirectoryView.setText(FileChooserUtil.folderPathFromData(data));
}
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.crash;
import android.os.Bundle;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.android.fbreader.util.SimpleDialogActivity;
public class MissingNativeLibraryActivity extends SimpleDialogActivity {
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
final ZLResource resource = ZLResource.resource("crash").getResource("missingNativeLibrary");
setTitle(resource.getResource("title").getValue());
textView().setText(resource.getResource("text").getValue());
okButton().setOnClickListener(finishListener());
setButtonTexts("ok", null);
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.dict;
import android.content.Intent;
import org.geometerplus.android.fbreader.FBReaderMainActivity;
final class ColorDict extends DictionaryUtil.PackageInfo {
private interface ColorDict3 {
String ACTION = "colordict.intent.action.SEARCH";
String QUERY = "EXTRA_QUERY";
String HEIGHT = "EXTRA_HEIGHT";
String WIDTH = "EXTRA_WIDTH";
String GRAVITY = "EXTRA_GRAVITY";
String MARGIN_LEFT = "EXTRA_MARGIN_LEFT";
String MARGIN_TOP = "EXTRA_MARGIN_TOP";
String MARGIN_BOTTOM = "EXTRA_MARGIN_BOTTOM";
String MARGIN_RIGHT = "EXTRA_MARGIN_RIGHT";
String FULLSCREEN = "EXTRA_FULLSCREEN";
}
ColorDict(String id, String title) {
super(id, title);
}
@Override
void open(String text, Runnable outliner, FBReaderMainActivity fbreader, DictionaryUtil.PopupFrameMetric frameMetrics) {
final Intent intent = getActionIntent(text);
intent.putExtra(ColorDict3.HEIGHT, frameMetrics.Height);
intent.putExtra(ColorDict3.GRAVITY, frameMetrics.Gravity);
intent.putExtra(ColorDict3.FULLSCREEN, !fbreader.getZLibrary().ShowStatusBarOption.getValue());
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
InternalUtil.startDictionaryActivity(fbreader, intent, this);
}
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.dict;
import com.github.johnpersano.supertoasts.SuperActivityToast;
import com.github.johnpersano.supertoasts.SuperToast;
import com.github.johnpersano.supertoasts.util.OnClickWrapper;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.os.Parcelable;
import android.view.View;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.android.fbreader.FBReaderMainActivity;
final class Dictan extends DictionaryUtil.PackageInfo {
private static final int MAX_LENGTH_FOR_TOAST = 180;
Dictan(String id, String title) {
super(id, title);
}
@Override
void open(String text, Runnable outliner, FBReaderMainActivity fbreader, DictionaryUtil.PopupFrameMetric frameMetrics) {
final Intent intent = getActionIntent(text);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
intent.putExtra("article.mode", 20);
intent.putExtra("article.text.size.max", MAX_LENGTH_FOR_TOAST);
try {
fbreader.startActivityForResult(intent, FBReaderMainActivity.REQUEST_DICTIONARY);
fbreader.overridePendingTransition(0, 0);
if (outliner != null) {
outliner.run();
}
} catch (ActivityNotFoundException e) {
InternalUtil.installDictionaryIfNotInstalled(fbreader, this);
}
}
void onActivityResult(final FBReaderMainActivity fbreader, int resultCode, final Intent data) {
if (data == null) {
fbreader.hideDictionarySelection();
return;
}
final int errorCode = data.getIntExtra("error.code", -1);
if (resultCode != FBReaderMainActivity.RESULT_OK || errorCode != -1) {
showError(fbreader, errorCode, data);
return;
}
String text = data.getStringExtra("article.text");
if (text == null) {
showError(fbreader, -1, data);
return;
}
// a hack for obsolete (before 5.0 beta) dictan versions
final int index = text.indexOf("\000");
if (index >= 0) {
text = text.substring(0, index);
}
final boolean hasExtraData;
if (text.length() == MAX_LENGTH_FOR_TOAST) {
text = trimArticle(text);
hasExtraData = true;
} else {
hasExtraData = data.getBooleanExtra("article.resources.contains", false);
}
final SuperActivityToast toast;
if (hasExtraData) {
toast = new SuperActivityToast(fbreader, SuperToast.Type.BUTTON);
toast.setButtonIcon(
android.R.drawable.ic_menu_more,
ZLResource.resource("toast").getResource("more").getValue()
);
toast.setOnClickWrapper(new OnClickWrapper("dict", new SuperToast.OnClickListener() {
@Override
public void onClick(View view, Parcelable token) {
final String word = data.getStringExtra("article.word");
final Intent intent = getActionIntent(word);
try {
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
fbreader.startActivity(intent);
fbreader.overridePendingTransition(0, 0);
} catch (ActivityNotFoundException e) {
// ignore
}
}
}));
} else {
toast = new SuperActivityToast(fbreader, SuperToast.Type.STANDARD);
}
toast.setText(text);
toast.setDuration(DictionaryUtil.TranslationToastDurationOption.getValue().Value);
InternalUtil.showToast(toast, fbreader);
}
private static String trimArticle(String text) {
final int len = text.length();
final int eolIndex = text.lastIndexOf("\n");
final int spaceIndex = text.lastIndexOf(" ");
if (spaceIndex < eolIndex || eolIndex >= len * 2 / 3) {
return text.substring(0, eolIndex);
} else {
return text.substring(0, spaceIndex);
}
}
private static void showError(final FBReaderMainActivity fbreader, int code, Intent data) {
final ZLResource resource = ZLResource.resource("dictanErrors");
String message;
switch (code) {
default:
message = data.getStringExtra("error.message");
if (message == null) {
message = resource.getResource("unknown").getValue();
}
break;
case 100:
{
final String word = data.getStringExtra("article.word");
message = resource.getResource("noArticle").getValue().replaceAll("%s", word);
break;
}
case 130:
message = resource.getResource("cannotOpenDictionary").getValue();
break;
case 131:
message = resource.getResource("noDictionarySelected").getValue();
break;
}
final SuperActivityToast toast = new SuperActivityToast(fbreader, SuperToast.Type.STANDARD);
toast.setText("Dictan: " + message);
toast.setDuration(DictionaryUtil.ErrorToastDurationOption.getValue().Value);
InternalUtil.showToast(toast, fbreader);
}
}

View file

@ -0,0 +1,106 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.dict;
import android.app.ListActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.*;
import android.widget.*;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.android.util.UIMessageUtil;
import org.geometerplus.android.util.PackageUtil;
public class DictionaryNotInstalledActivity extends ListActivity {
static final String DICTIONARY_NAME_KEY = "fbreader.dictionary.name";
static final String PACKAGE_NAME_KEY = "fbreader.package.name";
private ZLResource myResource;
private String myDictionaryName;
private String myPackageName;
@Override
protected void onCreate(Bundle saved) {
super.onCreate(saved);
myResource = ZLResource.resource("dialog").getResource("missingDictionary");
myDictionaryName = getIntent().getStringExtra(DICTIONARY_NAME_KEY);
myPackageName = getIntent().getStringExtra(PACKAGE_NAME_KEY);
setTitle(myResource.getValue().replaceAll("%s", myDictionaryName));
final Adapter adapter = new Adapter();
setListAdapter(adapter);
getListView().setOnItemClickListener(adapter);
}
private final class Adapter extends BaseAdapter implements AdapterView.OnItemClickListener {
private final String[] myItems = new String[] {
"install",
"configure",
"skip"
};
public int getCount() {
return myItems.length;
}
public String getItem(int position) {
return myItems[position];
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, final ViewGroup parent) {
final View view = convertView != null
? convertView
: LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_item, parent, false);
final TextView titleView = (TextView)view.findViewById(R.id.menu_item_title);
titleView.setText(
myResource.getResource(myItems[position]).getValue().replaceAll("%s", myDictionaryName)
);
return view;
}
public final void onItemClick(AdapterView<?> parent, View view, int position, long id) {
switch (position) {
case 0: // install
installDictionary();
break;
case 1: // configure
startActivity(new Intent(
Intent.ACTION_VIEW, Uri.parse("fbreader-action:preferences#dictionary")
));
break;
case 2: // skip
break;
}
finish();
}
}
private void installDictionary() {
if (!PackageUtil.installFromMarket(this, myPackageName)) {
UIMessageUtil.showErrorMessage(this, "cannotRunAndroidMarket", myDictionaryName);
}
}
}

View file

@ -0,0 +1,378 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.dict;
import java.util.*;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.app.Activity;
import android.content.*;
import android.net.Uri;
import android.util.DisplayMetrics;
import org.geometerplus.zlibrary.core.filesystem.ZLFile;
import org.geometerplus.zlibrary.core.language.Language;
import org.geometerplus.zlibrary.core.options.ZLEnumOption;
import org.geometerplus.zlibrary.core.options.ZLStringOption;
import org.geometerplus.zlibrary.core.util.XmlUtil;
import org.geometerplus.fbreader.fbreader.DurationEnum;
import org.geometerplus.android.fbreader.FBReaderMainActivity;
import org.geometerplus.android.util.PackageUtil;
public abstract class DictionaryUtil {
public static final ZLEnumOption<DurationEnum> TranslationToastDurationOption =
new ZLEnumOption<DurationEnum>("Dictionary", "TranslationToastDuration", DurationEnum.duration40);
public static final ZLEnumOption<DurationEnum> ErrorToastDurationOption =
new ZLEnumOption<DurationEnum>("Dictionary", "ErrorToastDuration", DurationEnum.duration5);
private static int FLAG_INSTALLED_ONLY = 1;
static int FLAG_SHOW_AS_DICTIONARY = 2;
private static int FLAG_SHOW_AS_TRANSLATOR = 4;
private static ZLStringOption ourSingleWordTranslatorOption;
private static ZLStringOption ourMultiWordTranslatorOption;
// TODO: use StringListOption instead
public static final ZLStringOption TargetLanguageOption = new ZLStringOption("Dictionary", "TargetLanguage", Language.ANY_CODE);
// Map: dictionary info -> mode if package is not installed
private static Map<PackageInfo,Integer> ourInfos =
Collections.synchronizedMap(new LinkedHashMap<PackageInfo,Integer>());
public static abstract class PackageInfo extends HashMap<String,String> {
public final boolean SupportsTargetLanguageSetting;
PackageInfo(String id, String title) {
this(id, title, false);
}
PackageInfo(String id, String title, boolean supportsTargetLanguageSetting) {
put("id", id);
put("title", title != null ? title : id);
SupportsTargetLanguageSetting = supportsTargetLanguageSetting;
}
public final String getId() {
return get("id");
}
public final String getTitle() {
return get("title");
}
final Intent getActionIntent(String text) {
final Intent intent = new Intent(get("action"));
final String packageName = get("package");
if (packageName != null) {
final String className = get("class");
if (className != null) {
intent.setComponent(new ComponentName(
packageName,
className.startsWith(".") ? packageName + className : className
));
}
}
final String category = get("category");
if (category != null) {
intent.addCategory(category);
}
final String key = get("dataKey");
if (key != null) {
return intent.putExtra(key, text);
} else {
return intent.setData(Uri.parse(text));
}
}
void onActivityResult(FBReaderMainActivity fbreader, int resultCode, final Intent data) {
// does nothing; implement in subclasses
}
abstract void open(String text, Runnable outliner, FBReaderMainActivity fbreader, PopupFrameMetric frameMetrics);
}
private static class PlainPackageInfo extends PackageInfo {
PlainPackageInfo(String id, String title) {
super(id, title);
}
@Override
void open(String text, Runnable outliner, FBReaderMainActivity fbreader, PopupFrameMetric frameMetrics) {
final Intent intent = getActionIntent(text);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
InternalUtil.startDictionaryActivity(fbreader, intent, this);
}
}
private static class InfoReader extends DefaultHandler {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (!"dictionary".equals(localName)) {
return;
}
final String id = attributes.getValue("id");
final String title = attributes.getValue("title");
final String role = attributes.getValue("role");
int flags;
if ("dictionary".equals(role)) {
flags = FLAG_SHOW_AS_DICTIONARY;
} else if ("translator".equals(role)) {
flags = FLAG_SHOW_AS_TRANSLATOR;
} else {
flags = FLAG_SHOW_AS_DICTIONARY | FLAG_SHOW_AS_TRANSLATOR;
}
if (!"always".equals(attributes.getValue("list"))) {
flags |= FLAG_INSTALLED_ONLY;
}
final PackageInfo info;
if ("dictan".equals(id)) {
info = new Dictan(id, title);
} else if ("ABBYY Lingvo".equals(id)) {
info = new Lingvo(id, title);
} else if ("ColorDict".equals(id)) {
info = new ColorDict(id, title);
} else {
info = new PlainPackageInfo(id, title);
}
for (int i = attributes.getLength() - 1; i >= 0; --i) {
info.put(attributes.getLocalName(i), attributes.getValue(i));
}
ourInfos.put(info, flags);
}
}
private static class BitKnightsInfoReader extends DefaultHandler {
private final Context myContext;
private int myCounter;
BitKnightsInfoReader(Context context) {
myContext = context;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (!"dictionary".equals(localName)) {
return;
}
final PackageInfo info = new PlainPackageInfo(
"BK" + myCounter ++,
attributes.getValue("title")
);
for (int i = attributes.getLength() - 1; i >= 0; --i) {
info.put(attributes.getLocalName(i), attributes.getValue(i));
}
info.put("class", "com.bitknights.dict.ShareTranslateActivity");
info.put("action", Intent.ACTION_VIEW);
// TODO: other attributes
if (PackageUtil.canBeStarted(myContext, info.getActionIntent("test"), false)) {
ourInfos.put(info, FLAG_SHOW_AS_DICTIONARY | FLAG_INSTALLED_ONLY);
}
}
}
private static final class Initializer implements Runnable {
private final Activity myActivity;
private final Runnable myPostAction;
public Initializer(Activity activity, Runnable postAction) {
myActivity = activity;
myPostAction = postAction;
}
public void run() {
synchronized (ourInfos) {
if (!ourInfos.isEmpty()) {
if (myPostAction != null) {
myPostAction.run();
}
return;
}
XmlUtil.parseQuietly(
ZLFile.createFileByPath("dictionaries/main.xml"),
new InfoReader()
);
XmlUtil.parseQuietly(
ZLFile.createFileByPath("dictionaries/bitknights.xml"),
new BitKnightsInfoReader(myActivity)
);
myActivity.runOnUiThread(new Runnable() {
public void run() {
OpenDictionary.collect(myActivity, ourInfos);
if (myPostAction != null) {
myPostAction.run();
}
}
});
}
}
}
public static void init(Activity activity, Runnable postAction) {
if (ourInfos.isEmpty()) {
final Thread initThread = new Thread(new Initializer(activity, postAction));
initThread.setPriority(Thread.MIN_PRIORITY);
initThread.start();
} else if (postAction != null) {
postAction.run();
}
}
public static List<PackageInfo> dictionaryInfos(Context context, boolean dictionaryNotTranslator) {
final LinkedList<PackageInfo> list = new LinkedList<PackageInfo>();
final HashSet<String> installedPackages = new HashSet<String>();
final HashSet<String> notInstalledPackages = new HashSet<String>();
synchronized (ourInfos) {
for (Map.Entry<PackageInfo,Integer> entry : ourInfos.entrySet()) {
final PackageInfo info = entry.getKey();
final int flags = entry.getValue();
if (dictionaryNotTranslator) {
if ((flags & FLAG_SHOW_AS_DICTIONARY) == 0) {
continue;
}
} else {
if ((flags & FLAG_SHOW_AS_TRANSLATOR) == 0) {
continue;
}
}
final String packageName = info.get("package");
if (((flags & FLAG_INSTALLED_ONLY) == 0) ||
installedPackages.contains(packageName)) {
list.add(info);
} else if (!notInstalledPackages.contains(packageName)) {
if (PackageUtil.canBeStarted(context, info.getActionIntent("test"), false)) {
list.add(info);
installedPackages.add(packageName);
} else {
notInstalledPackages.add(packageName);
}
}
}
}
return list;
}
private static PackageInfo firstInfo() {
synchronized (ourInfos) {
for (Map.Entry<PackageInfo,Integer> entry : ourInfos.entrySet()) {
if ((entry.getValue() & FLAG_INSTALLED_ONLY) == 0) {
return entry.getKey();
}
}
}
throw new RuntimeException("There are no available dictionary infos");
}
public static ZLStringOption singleWordTranslatorOption() {
if (ourSingleWordTranslatorOption == null) {
ourSingleWordTranslatorOption = new ZLStringOption("Dictionary", "Id", firstInfo().getId());
}
return ourSingleWordTranslatorOption;
}
public static ZLStringOption multiWordTranslatorOption() {
if (ourMultiWordTranslatorOption == null) {
ourMultiWordTranslatorOption = new ZLStringOption("Translator", "Id", firstInfo().getId());
}
return ourMultiWordTranslatorOption;
}
private static PackageInfo getDictionaryInfo(String id) {
if (id == null) {
return firstInfo();
}
synchronized (ourInfos) {
for (PackageInfo info : ourInfos.keySet()) {
if (id.equals(info.getId())) {
return info;
}
}
}
return firstInfo();
}
public static PackageInfo getCurrentDictionaryInfo(boolean singleWord) {
final ZLStringOption option = singleWord
? singleWordTranslatorOption() : multiWordTranslatorOption();
return getDictionaryInfo(option.getValue());
}
public static class PopupFrameMetric {
public final int Height;
public final int Gravity;
PopupFrameMetric(DisplayMetrics metrics, int selectionTop, int selectionBottom) {
final int screenHeight = metrics.heightPixels;
final int topSpace = selectionTop;
final int bottomSpace = metrics.heightPixels - selectionBottom;
final boolean showAtBottom = bottomSpace >= topSpace;
final int space = (showAtBottom ? bottomSpace : topSpace) - metrics.densityDpi / 12;
final int maxHeight = Math.min(metrics.densityDpi * 20 / 12, screenHeight * 2 / 3);
final int minHeight = Math.min(metrics.densityDpi * 10 / 12, screenHeight * 2 / 3);
Height = Math.max(minHeight, Math.min(maxHeight, space));
Gravity = showAtBottom ? android.view.Gravity.BOTTOM : android.view.Gravity.TOP;
}
}
public static void openTextInDictionary(final FBReaderMainActivity fbreader, String text, boolean singleWord, int selectionTop, int selectionBottom, final Runnable outliner) {
final String textToTranslate;
if (singleWord) {
int start = 0;
int end = text.length();
for (; start < end && !Character.isLetterOrDigit(text.charAt(start)); ++start);
for (; start < end && !Character.isLetterOrDigit(text.charAt(end - 1)); --end);
if (start == end) {
return;
}
textToTranslate = text.substring(start, end);
} else {
textToTranslate = text;
}
final DisplayMetrics metrics = new DisplayMetrics();
fbreader.getWindowManager().getDefaultDisplay().getMetrics(metrics);
final PopupFrameMetric frameMetrics =
new PopupFrameMetric(metrics, selectionTop, selectionBottom);
final PackageInfo info = getCurrentDictionaryInfo(singleWord);
fbreader.runOnUiThread(new Runnable() {
public void run() {
info.open(textToTranslate, outliner, fbreader, frameMetrics);
}
});
}
public static void onActivityResult(final FBReaderMainActivity fbreader, int resultCode, final Intent data) {
getDictionaryInfo("dictan").onActivityResult(fbreader, resultCode, data);
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.dict;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.view.View;
import com.github.johnpersano.supertoasts.SuperActivityToast;
import com.github.johnpersano.supertoasts.SuperToast;
import com.github.johnpersano.supertoasts.util.OnDismissWrapper;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.android.fbreader.FBReaderMainActivity;
import org.geometerplus.android.util.PackageUtil;
abstract class InternalUtil {
static void installDictionaryIfNotInstalled(final Activity activity, final DictionaryUtil.PackageInfo info) {
if (PackageUtil.canBeStarted(activity, info.getActionIntent("test"), false)) {
return;
}
final Intent intent = new Intent(activity, DictionaryNotInstalledActivity.class);
intent.putExtra(DictionaryNotInstalledActivity.DICTIONARY_NAME_KEY, info.getTitle());
intent.putExtra(DictionaryNotInstalledActivity.PACKAGE_NAME_KEY, info.get("package"));
activity.startActivity(intent);
}
static void startDictionaryActivity(FBReaderMainActivity fbreader, Intent intent, DictionaryUtil.PackageInfo info) {
try {
fbreader.startActivity(intent);
fbreader.overridePendingTransition(0, 0);
} catch (ActivityNotFoundException e) {
installDictionaryIfNotInstalled(fbreader, info);
}
}
static void showToast(SuperActivityToast toast, final FBReaderMainActivity fbreader) {
toast.setOnDismissWrapper(new OnDismissWrapper("dict", new SuperToast.OnDismissListener() {
@Override
public void onDismiss(View view) {
fbreader.hideDictionarySelection();
}
}));
fbreader.showToast(toast);
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.dict;
import android.content.Intent;
import com.abbyy.mobile.lingvo.api.MinicardContract;
import org.geometerplus.zlibrary.core.language.Language;
import org.geometerplus.android.fbreader.FBReaderMainActivity;
final class Lingvo extends DictionaryUtil.PackageInfo {
Lingvo(String id, String title) {
super(id, title, true);
}
@Override
void open(String text, Runnable outliner, FBReaderMainActivity fbreader, DictionaryUtil.PopupFrameMetric frameMetrics) {
final Intent intent = getActionIntent(text);
intent.putExtra(MinicardContract.EXTRA_GRAVITY, frameMetrics.Gravity);
intent.putExtra(MinicardContract.EXTRA_HEIGHT, frameMetrics.Height);
intent.putExtra(MinicardContract.EXTRA_FORCE_LEMMATIZATION, true);
intent.putExtra(MinicardContract.EXTRA_TRANSLATE_VARIANTS, true);
intent.putExtra(MinicardContract.EXTRA_LIGHT_THEME, true);
final String targetLanguage = DictionaryUtil.TargetLanguageOption.getValue();
if (!Language.ANY_CODE.equals(targetLanguage)) {
intent.putExtra(MinicardContract.EXTRA_LANGUAGE_TO, targetLanguage);
}
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
InternalUtil.startDictionaryActivity(fbreader, intent, this);
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.dict;
import java.util.*;
import android.content.Context;
import com.paragon.dictionary.fbreader.OpenDictionaryFlyout;
import com.paragon.open.dictionary.api.Dictionary;
import com.paragon.open.dictionary.api.OpenDictionaryAPI;
import org.geometerplus.android.fbreader.FBReaderMainActivity;
final class OpenDictionary extends DictionaryUtil.PackageInfo {
static void collect(Context context, Map<DictionaryUtil.PackageInfo,Integer> dictMap) {
final SortedSet<Dictionary> dictionariesTreeSet =
new TreeSet<Dictionary>(new Comparator<Dictionary>() {
@Override
public int compare(Dictionary lhs, Dictionary rhs) {
return lhs.toString().compareTo(rhs.toString());
}
}
);
dictionariesTreeSet.addAll(
new OpenDictionaryAPI(context).getDictionaries()
);
for (Dictionary dict : dictionariesTreeSet) {
dictMap.put(new OpenDictionary(dict), DictionaryUtil.FLAG_SHOW_AS_DICTIONARY);
}
}
final OpenDictionaryFlyout Flyout;
OpenDictionary(Dictionary dictionary) {
super(dictionary.getUID(), dictionary.getName());
put("package", dictionary.getApplicationPackageName());
put("class", ".Start");
Flyout = new OpenDictionaryFlyout(dictionary);
}
@Override
void open(String text, Runnable outliner, FBReaderMainActivity fbreader, DictionaryUtil.PopupFrameMetric frameMetrics) {
Flyout.showTranslation(fbreader, text, frameMetrics);
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.error;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.ui.android.error.ErrorKeys;
import org.geometerplus.zlibrary.ui.android.error.ErrorUtil;
import org.geometerplus.android.fbreader.util.SimpleDialogActivity;
public class BookReadingErrorActivity extends SimpleDialogActivity implements ErrorKeys {
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
final ZLResource resource = ZLResource.resource("error").getResource("bookReading");
setTitle(resource.getResource("title").getValue());
textView().setText(getIntent().getStringExtra(MESSAGE));
okButton().setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
final Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { "issues@fbreader.org" });
sendIntent.putExtra(Intent.EXTRA_TEXT, getIntent().getStringExtra(STACKTRACE));
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "FBReader " + new ErrorUtil(BookReadingErrorActivity.this).getVersionName() + " book reading issue report");
sendIntent.setType("message/rfc822");
startActivity(sendIntent);
finish();
}
});
cancelButton().setOnClickListener(finishListener());
setButtonTexts("sendReport", "cancel");
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.formatPlugin;
import android.graphics.Bitmap;
interface CoverReader {
Bitmap readBitmap(in String path, in int maxWidth, in int maxHeight);
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.formatPlugin;
import android.content.Intent;
import org.geometerplus.fbreader.formats.ExternalFormatPlugin;
public abstract class PluginUtil {
public static Intent createIntent(ExternalFormatPlugin plugin, String action) {
return new Intent(action).setPackage(plugin.packageName());
}
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.httpd;
interface DataInterface {
int getPort();
}

View file

@ -0,0 +1,226 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.httpd;
import java.io.*;
import java.util.Map;
import fi.iki.elonen.NanoHTTPD;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import org.geometerplus.zlibrary.core.filesystem.ZLFile;
import org.geometerplus.zlibrary.core.image.*;
import org.geometerplus.zlibrary.core.util.MimeType;
import org.geometerplus.zlibrary.core.util.SliceInputStream;
import org.geometerplus.zlibrary.ui.android.image.ZLBitmapImage;
import org.geometerplus.fbreader.Paths;
import org.geometerplus.fbreader.book.CoverUtil;
import org.geometerplus.fbreader.formats.PluginCollection;
import org.geometerplus.fbreader.formats.PluginImage;
public class DataServer extends NanoHTTPD {
private final DataService myService;
DataServer(DataService service, int port) {
super(port);
myService = service;
}
@Override
public Response serve(String uri, Method method, Map<String,String> headers, Map<String,String> params, Map<String,String> files) {
if (uri.startsWith("/cover/")) {
return serveCover(uri, method, headers, params, files);
} else if (uri.startsWith("/video")) {
return serveVideo(uri, method, headers, params, files);
} else {
return notFound(uri);
}
}
private Response serveCover(String uri, Method method, Map<String,String> headers, Map<String,String> params, Map<String,String> files) {
try {
final ZLImage image = CoverUtil.getCover(
DataUtil.fileFromEncodedPath(uri.substring(7)),
PluginCollection.Instance(Paths.systemInfo(myService))
);
if (image instanceof ZLFileImageProxy) {
final ZLFileImageProxy proxy = (ZLFileImageProxy)image;
proxy.synchronize();
final ZLStreamImage realImage = proxy.getRealImage();
if (realImage == null) {
return notFound(uri);
}
InputStream stream = realImage.inputStream();
if (stream == null) {
return notFound(uri);
}
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
try {
BitmapFactory.decodeStream(stream, null, options);
} catch (Exception e) {
return notFound(uri);
}
if (options.outWidth <= 0 || options.outHeight <= 0) {
return notFound(uri);
}
stream.close();
stream = realImage.inputStream();
if (stream == null) {
return notFound(uri);
}
final Response res =
new Response(Response.Status.OK, MimeType.IMAGE_PNG.toString(), stream);
res.addHeader("X-Width", String.valueOf(options.outWidth));
res.addHeader("X-Height", String.valueOf(options.outHeight));
return res;
} else if (image instanceof PluginImage) {
final PluginImage pluginImage = (PluginImage)image;
if (pluginImage.isSynchronized()) {
try {
final Bitmap bitmap =
((ZLBitmapImage)pluginImage.getRealImage()).getBitmap();
final ByteArrayOutputStream os = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, os);
final InputStream is = new ByteArrayInputStream(os.toByteArray());
final Response res =
new Response(Response.Status.OK, MimeType.IMAGE_JPEG.toString(), is);
res.addHeader("X-Width", String.valueOf(bitmap.getWidth()));
res.addHeader("X-Height", String.valueOf(bitmap.getHeight()));
return res;
} catch (Throwable t) {
return noContent(uri);
}
} else {
myService.ImageSynchronizer.synchronize(pluginImage, null);
return noContent(uri);
}
} else {
return notFound(uri);
}
} catch (Throwable t) {
return forbidden(uri, t);
}
}
private Response serveVideo(String uri, Method method, Map<String,String> headers, Map<String,String> params, Map<String,String> files) {
String mime = null;
for (MimeType mimeType : MimeType.TYPES_VIDEO) {
final String m = mimeType.toString();
if (uri.startsWith("/" + m + "/")) {
mime = m;
break;
}
}
if (mime == null) {
return notFound(uri);
}
try {
return serveFile(DataUtil.fileFromEncodedPath(uri.substring(mime.length() + 2)), mime, headers);
} catch (Exception e) {
return forbidden(uri, e);
}
}
private static final String BYTES_PREFIX = "bytes=";
private Response serveFile(ZLFile file, String mime, Map<String,String> headers) throws IOException {
final Response res;
final InputStream baseStream = file.getInputStream();
final int fileLength = baseStream.available();
final String etag = '"' + Integer.toHexString(file.getPath().hashCode()) + '"';
final String range = headers.get("range");
if (range == null || !range.startsWith(BYTES_PREFIX)) {
if (etag.equals(headers.get("if-none-match")))
res = new Response(Response.Status.NOT_MODIFIED, mime, "");
else {
res = new Response(Response.Status.OK, mime, baseStream);
res.addHeader("ETag", etag);
}
} else {
int start = 0;
int end = -1;
final String bytes = range.substring(BYTES_PREFIX.length());
final int minus = bytes.indexOf('-');
if (minus > 0) {
try {
start = Integer.parseInt(bytes.substring(0, minus));
final String endString = bytes.substring(minus + 1).trim();
if (!"".equals(endString)) {
end = Integer.parseInt(endString);
}
} catch (NumberFormatException e) {
}
}
if (start >= fileLength) {
res = new Response(
Response.Status.RANGE_NOT_SATISFIABLE,
MimeType.TEXT_PLAIN.toString(),
""
);
res.addHeader("ETag", etag);
res.addHeader("Content-Range", "bytes 0-0/" + fileLength);
} else {
if (end == -1 || end >= fileLength) {
end = fileLength - 1;
}
res = new Response(
Response.Status.PARTIAL_CONTENT,
mime,
new SliceInputStream(baseStream, start, end - start + 1)
);
res.addHeader("ETag", etag);
res.addHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength);
}
}
res.addHeader("Accept-Ranges", "bytes");
return res;
}
private Response notFound(String uri) {
return new Response(
Response.Status.NOT_FOUND,
MimeType.TEXT_HTML.toString(),
"<html><body><h1>Not found: " + uri + "</h1></body></html>"
);
}
private Response noContent(String uri) {
return new Response(
Response.Status.NO_CONTENT,
MimeType.TEXT_HTML.toString(),
"<html><body><h1>No content: " + uri + "</h1></body></html>"
);
}
private Response forbidden(String uri, Throwable t) {
t.printStackTrace();
return new Response(
Response.Status.FORBIDDEN,
MimeType.TEXT_HTML.toString(),
"<html><body><h1>" + t.getMessage() + "</h1>\n(" + uri + ")</body></html>"
);
}
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.httpd;
import java.io.IOException;
import android.app.Service;
import android.content.*;
import android.os.IBinder;
import android.os.RemoteException;
import org.geometerplus.android.fbreader.util.AndroidImageSynchronizer;
public class DataService extends Service {
final AndroidImageSynchronizer ImageSynchronizer = new AndroidImageSynchronizer(this);
public static class Connection implements ServiceConnection {
private DataInterface myDataInterface;
public void onServiceConnected(ComponentName componentName, IBinder binder) {
myDataInterface = DataInterface.Stub.asInterface(binder);
}
public void onServiceDisconnected(ComponentName componentName) {
myDataInterface = null;
}
public int getPort() {
try {
return myDataInterface != null ? myDataInterface.getPort() : -1;
} catch (RemoteException e) {
return -1;
}
}
}
private DataServer myServer;
private volatile int myPort = -1;
@Override
public void onCreate() {
new Thread(new Runnable() {
public void run () {
for (int port = 12000; port < 12500; ++port) {
try {
myServer = new DataServer(DataService.this, port);
myServer.start();
myPort = port;
break;
} catch (IOException e) {
myServer = null;
}
}
}
}).start();
}
@Override
public void onDestroy() {
if (myServer != null) {
new Thread(new Runnable() {
public void run () {
if (myServer != null) {
myServer.stop();
myServer = null;
}
}
}).start();
}
ImageSynchronizer.clear();
super.onDestroy();
}
public IBinder onBind(Intent intent) {
return new DataInterface.Stub() {
public int getPort() {
return myPort;
}
};
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.httpd;
import org.geometerplus.zlibrary.core.filesystem.ZLFile;
public abstract class DataUtil {
static ZLFile fileFromEncodedPath(String encodedPath) {
final StringBuilder path = new StringBuilder();
for (String item : encodedPath.split("X")) {
if (item.length() == 0) {
continue;
}
path.append((char)Short.parseShort(item, 16));
}
return ZLFile.createFileByPath(path.toString());
}
public static String buildUrl(DataService.Connection connection, String prefix, String path) {
final int port = connection.getPort();
if (port == -1) {
return null;
}
final StringBuilder url = new StringBuilder("http://127.0.0.1:").append(port)
.append("/").append(prefix).append("/");
for (int i = 0; i < path.length(); ++i) {
url.append(String.format("X%X", (short)path.charAt(i)));
}
return url.toString();
}
}

View file

@ -0,0 +1,270 @@
/*
* Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.image;
import android.app.Activity;
import android.content.Intent;
import android.graphics.*;
import android.os.Bundle;
import android.util.FloatMath;
import android.view.*;
import org.geometerplus.zlibrary.core.image.*;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.ui.android.library.ZLAndroidLibrary;
import org.geometerplus.zlibrary.ui.android.image.ZLAndroidImageData;
import org.geometerplus.zlibrary.ui.android.util.ZLAndroidColorUtil;
import org.geometerplus.android.fbreader.OrientationUtil;
public class ImageViewActivity extends Activity {
public static final String URL_KEY = "fbreader.imageview.url";
public static final String BACKGROUND_COLOR_KEY = "fbreader.imageview.background";
private Bitmap myBitmap;
private ZLColor myBgColor;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
requestWindowFeature(Window.FEATURE_NO_TITLE);
final ZLAndroidLibrary library = (ZLAndroidLibrary)ZLAndroidLibrary.Instance();
final boolean showStatusBar = library.ShowStatusBarOption.getValue();
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
showStatusBar ? 0 : WindowManager.LayoutParams.FLAG_FULLSCREEN
);
Thread.setDefaultUncaughtExceptionHandler(
new org.geometerplus.zlibrary.ui.android.library.UncaughtExceptionHandler(this)
);
setContentView(new ImageView());
final Intent intent = getIntent();
myBgColor = new ZLColor(
intent.getIntExtra(BACKGROUND_COLOR_KEY, new ZLColor(127, 127, 127).intValue())
);
final String url = intent.getStringExtra(URL_KEY);
final String prefix = ZLFileImage.SCHEME + "://";
if (url != null && url.startsWith(prefix)) {
final ZLFileImage image = ZLFileImage.byUrlPath(url.substring(prefix.length()));
if (image == null) {
// TODO: error message (?)
finish();
}
try {
final ZLImageData imageData = ZLImageManager.Instance().getImageData(image);
myBitmap = ((ZLAndroidImageData)imageData).getFullSizeBitmap();
} catch (Exception e) {
// TODO: error message (?)
e.printStackTrace();
finish();
}
} else {
// TODO: error message (?)
finish();
}
}
@Override
protected void onStart() {
super.onStart();
OrientationUtil.setOrientation(this, getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
OrientationUtil.setOrientation(this, intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (myBitmap != null) {
myBitmap.recycle();
}
myBitmap = null;
}
private class ImageView extends View {
private final Paint myPaint = new Paint();
private volatile int myDx = 0;
private volatile int myDy = 0;
private volatile float myZoomFactor = 1.0f;
ImageView() {
super(ImageViewActivity.this);
}
@Override
protected void onDraw(final Canvas canvas) {
myPaint.setColor(ZLAndroidColorUtil.rgb(myBgColor));
final int w = getWidth();
final int h = getHeight();
canvas.drawRect(0, 0, w, h, myPaint);
if (myBitmap == null || myBitmap.isRecycled()) {
return;
}
final int bw = (int)(myBitmap.getWidth() * myZoomFactor);
final int bh = (int)(myBitmap.getHeight() * myZoomFactor);
final Rect src = new Rect(0, 0, (int)(w / myZoomFactor), (int)(h / myZoomFactor));
final Rect dst = new Rect(0, 0, w, h);
if (bw <= w) {
src.left = 0;
src.right = myBitmap.getWidth();
dst.left = (w - bw) / 2;
dst.right = dst.left + bw;
} else {
final int bWidth = myBitmap.getWidth();
final int pWidth = (int)(w / myZoomFactor);
src.left = Math.min(bWidth - pWidth, Math.max((bWidth - pWidth) / 2 - myDx, 0));
src.right += src.left;
}
if (bh <= h) {
src.top = 0;
src.bottom = myBitmap.getHeight();
dst.top = (h - bh) / 2;
dst.bottom = dst.top + bh;
} else {
final int bHeight = myBitmap.getHeight();
final int pHeight = (int)(h / myZoomFactor);
src.top = Math.min(bHeight - pHeight, Math.max((bHeight - pHeight) / 2 - myDy, 0));
src.bottom += src.top;
}
canvas.drawBitmap(myBitmap, src, dst, myPaint);
}
private void shift(int dx, int dy) {
if (myBitmap == null || myBitmap.isRecycled()) {
return;
}
final int w = (int)(getWidth() / myZoomFactor);
final int h = (int)(getHeight() / myZoomFactor);
final int bw = myBitmap.getWidth();
final int bh = myBitmap.getHeight();
final int newDx, newDy;
if (w < bw) {
final int delta = (bw - w) / 2;
newDx = Math.max(-delta, Math.min(delta, myDx + dx));
} else {
newDx = myDx;
}
if (h < bh) {
final int delta = (bh - h) / 2;
newDy = Math.max(-delta, Math.min(delta, myDy + dy));
} else {
newDy = myDy;
}
if (newDx != myDx || newDy != myDy) {
myDx = newDx;
myDy = newDy;
postInvalidate();
}
}
private boolean myMotionControl;
private int mySavedX;
private int mySavedY;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getPointerCount()) {
case 1:
return onSingleTouchEvent(event);
case 2:
return onDoubleTouchEvent(event);
default:
return false;
}
}
private boolean onSingleTouchEvent(MotionEvent event) {
int x = (int)event.getX();
int y = (int)event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
myMotionControl = false;
break;
case MotionEvent.ACTION_DOWN:
myMotionControl = true;
mySavedX = x;
mySavedY = y;
break;
case MotionEvent.ACTION_MOVE:
if (myMotionControl) {
shift(
(int)((x - mySavedX) / myZoomFactor),
(int)((y - mySavedY) / myZoomFactor)
);
}
myMotionControl = true;
mySavedX = x;
mySavedY = y;
break;
}
return true;
}
private float myStartPinchDistance2 = -1;
private float myStartZoomFactor;
private boolean onDoubleTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_UP:
myStartPinchDistance2 = -1;
break;
case MotionEvent.ACTION_POINTER_DOWN:
{
final float diffX = event.getX(0) - event.getX(1);
final float diffY = event.getY(0) - event.getY(1);
myStartPinchDistance2 = Math.max(diffX * diffX + diffY * diffY, 10f);
myStartZoomFactor = myZoomFactor;
break;
}
case MotionEvent.ACTION_MOVE:
{
final float diffX = event.getX(0) - event.getX(1);
final float diffY = event.getY(0) - event.getY(1);
final float distance2 = Math.max(diffX * diffX + diffY * diffY, 10f);
if (myStartPinchDistance2 < 0) {
myStartPinchDistance2 = distance2;
myStartZoomFactor = myZoomFactor;
} else {
myZoomFactor = myStartZoomFactor * FloatMath.sqrt(distance2 / myStartPinchDistance2);
postInvalidate();
}
}
break;
}
return true;
}
}
}

Some files were not shown because too many files have changed in this diff Show more