diff --git a/.gitignore b/.gitignore index 7d168dc..9acd65d 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,7 @@ local.properties # Gradle files .gradle/ build/ - +readme.txt # Local configuration file (sdk path, etc) local.properties @@ -35,4 +35,4 @@ local.properties proguard/ # Log Files -*.log \ No newline at end of file +*.log diff --git a/README.md b/README.md index e69de29..1fb7358 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,20 @@ +## 英文原著阅读器 + +英文原著阅读器,可以通过单击单词查看释义、发音、例句。 + +### LICENSE + +Copyright (C) 2018 wayne <15901418657@163.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . diff --git a/ambilWarna/build.gradle b/ambilWarna/build.gradle index 1b593ab..f16db04 100644 --- a/ambilWarna/build.gradle +++ b/ambilWarna/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' android { compileSdkVersion 19 - buildToolsVersion "25.0.1" + buildToolsVersion '27.0.3' defaultConfig { minSdkVersion 8 @@ -14,5 +14,10 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } + debug { + + } + sourceSets { + } } } diff --git a/build.gradle b/build.gradle index f6627fc..af48343 100644 --- a/build.gradle +++ b/build.gradle @@ -2,14 +2,22 @@ buildscript { repositories { jcenter() + maven { + url 'https://maven.google.com/' + name 'Google' + } } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' + classpath 'com.android.tools.build:gradle:3.1.3' } } allprojects { repositories { jcenter() + maven { + url 'https://maven.google.com/' + name 'Google' + } } } diff --git a/code/build.gradle b/code/build.gradle index 1b593ab..f16db04 100644 --- a/code/build.gradle +++ b/code/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' android { compileSdkVersion 19 - buildToolsVersion "25.0.1" + buildToolsVersion '27.0.3' defaultConfig { minSdkVersion 8 @@ -14,5 +14,10 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } + debug { + + } + sourceSets { + } } } diff --git a/fBReader/ReadMe.txt b/fBReader/ReadMe.txt new file mode 100644 index 0000000..0039e5f --- /dev/null +++ b/fBReader/ReadMe.txt @@ -0,0 +1,5 @@ +1.向上向下滑动出menu按钮,点击返回或者再次点击消失 + +-ZLTextRegion findRegion 返回字的内容可以利用它来完成后续翻译内容参数。 +-2。左侧点击右侧点击翻页取消, +-3。单击换成dialog单词翻译。 \ No newline at end of file diff --git a/fBReader/build.gradle b/fBReader/build.gradle index f454f06..babea1c 100644 --- a/fBReader/build.gradle +++ b/fBReader/build.gradle @@ -1,24 +1,30 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 19 - buildToolsVersion "25.0.1" - + compileSdkVersion 25 + buildToolsVersion '26.0.2' + useLibrary 'org.apache.http.legacy' defaultConfig { applicationId "org.geometerplus.zlibrary.ui.android" - minSdkVersion 8 - targetSdkVersion 19 + minSdkVersion 18 + targetSdkVersion 25 ndk { - moduleName "DeflatingDecompressor-v3" +// moduleName "DeflatingDecompressor-v3" + abiFilters "armeabi", "armeabi-v7a", "x86", "mips" } +// vectorDrawables.useSupportLibrary = true } buildTypes { + debug { + + } release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } + sourceSets { main { jni.srcDirs = [] @@ -28,15 +34,22 @@ android { } dependencies { - compile project(':ambilWarna') - compile project(':code') - compile project(':library') - compile project(':superToasts') - compile 'com.android.support:support-v4:19.1.0' + testCompile 'junit:junit:4.12' + compile 'com.android.support:support-v4:25.0.1' compile files('libs/LingvoIntegration_2.5.2.12.jar') compile files('libs/httpmime-4.2.5.jar') compile files('libs/json-simple-1.1.1.jar') compile files('libs/nanohttpd-2.0.5.jar') compile files('libs/open-dictionary-api-1.2.1.jar') compile files('libs/pdfparse.jar') -} + implementation project(':ambilWarna') + implementation project(':code') + implementation project(':library') + implementation project(':superToasts') + implementation project(':translate_bd') +// compile 'com.google.code.gson:gson:2.3.0' + compile 'com.squareup.okhttp3:okhttp:3.9.1' + compile 'com.squareup.retrofit2:retrofit:2.3.0' + compile 'com.squareup.retrofit2:converter-gson:2.3.0' + compile 'pub.devrel:easypermissions:0.1.5' +} \ No newline at end of file diff --git a/fBReader/src/main/AndroidManifest.xml b/fBReader/src/main/AndroidManifest.xml index 4ecd902..ad8b2c4 100644 --- a/fBReader/src/main/AndroidManifest.xml +++ b/fBReader/src/main/AndroidManifest.xml @@ -30,6 +30,8 @@ + + diff --git a/fBReader/src/main/java/dyg/activity/TransWebViewActivity.java b/fBReader/src/main/java/dyg/activity/TransWebViewActivity.java new file mode 100644 index 0000000..08acb15 --- /dev/null +++ b/fBReader/src/main/java/dyg/activity/TransWebViewActivity.java @@ -0,0 +1,27 @@ +package dyg.activity; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.webkit.WebView; +import android.widget.Toast; + +import org.geometerplus.zlibrary.ui.android.R; + +public class TransWebViewActivity extends Activity { + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main_tranlate_web); + Intent intent = getIntent(); + String string = intent.getStringExtra("key"); + if (string == null) { + Toast.makeText(this, "no key word ,can't translate", Toast.LENGTH_SHORT).show(); + finish(); + } + WebView webView = (WebView) findViewById(R.id.layout_webview); + webView.loadUrl("http://apii.dict.cn/mini.php?q=" + string); + + } +} diff --git a/fBReader/src/main/java/dyg/beans/CiBaWordBeanJson.java b/fBReader/src/main/java/dyg/beans/CiBaWordBeanJson.java new file mode 100644 index 0000000..a1d53b0 --- /dev/null +++ b/fBReader/src/main/java/dyg/beans/CiBaWordBeanJson.java @@ -0,0 +1,237 @@ +package dyg.beans; + +import java.util.List; + +public class CiBaWordBeanJson { + + /** + * word_name : hello + * is_CRI : 1 + * exchange : {"word_pl":["hellos"],"word_past":"","word_done":"","word_ing":"","word_third":"","word_er":"","word_est":""} + * symbols : [{"ph_en":"hə'ləʊ","ph_am":"həˈloʊ","ph_other":"","ph_en_mp3":"","ph_am_mp3":"http://res.iciba.com/resource/amp3/1/0/5d/41/5d41402abc4b2a76b9719d911017c592.mp3","ph_tts_mp3":"http://res-tts.iciba.com/5/d/4/5d41402abc4b2a76b9719d911017c592.mp3","parts":[{"part":"int.","means":["哈喽,喂","你好,您好","表示问候","打招呼"]},{"part":"n.","means":["\u201c喂\u201d的招呼声或问候声"]},{"part":"vi.","means":["喊\u201c喂\u201d"]}]}] + * items : [""] + */ + + private String word_name; + private int is_CRI; + private ExchangeBean exchange; + private List symbols; + private List items; + + public String getWord_name() { + return word_name; + } + + public void setWord_name(String word_name) { + this.word_name = word_name; + } + + public int getIs_CRI() { + return is_CRI; + } + + public void setIs_CRI(int is_CRI) { + this.is_CRI = is_CRI; + } + + public ExchangeBean getExchange() { + return exchange; + } + + public void setExchange(ExchangeBean exchange) { + this.exchange = exchange; + } + + public List getSymbols() { + return symbols; + } + + public void setSymbols(List symbols) { + this.symbols = symbols; + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + public static class ExchangeBean { + /** + * word_pl : ["hellos"] + * word_past : + * word_done : + * word_ing : + * word_third : + * word_er : + * word_est : + */ + + private List word_past; + private List word_done; + private List word_ing; + private List word_third; + private List word_er; + private List word_est; + private List word_pl; + + public List getWord_past() { + return word_past; + } + + public void setWord_past(List word_past) { + this.word_past = word_past; + } + + public List getWord_done() { + return word_done; + } + + public void setWord_done(List word_done) { + this.word_done = word_done; + } + + public List getWord_ing() { + return word_ing; + } + + public void setWord_ing(List word_ing) { + this.word_ing = word_ing; + } + + public List getWord_third() { + return word_third; + } + + public void setWord_third(List word_third) { + this.word_third = word_third; + } + + public List getWord_er() { + return word_er; + } + + public void setWord_er(List word_er) { + this.word_er = word_er; + } + + public List getWord_est() { + return word_est; + } + + public void setWord_est(List word_est) { + this.word_est = word_est; + } + + public List getWord_pl() { + return word_pl; + } + + public void setWord_pl(List word_pl) { + this.word_pl = word_pl; + } + } + + public static class SymbolsBean { + /** + * ph_en : hə'ləʊ + * ph_am : həˈloʊ + * ph_other : + * ph_en_mp3 : + * ph_am_mp3 : http://res.iciba.com/resource/amp3/1/0/5d/41/5d41402abc4b2a76b9719d911017c592.mp3 + * ph_tts_mp3 : http://res-tts.iciba.com/5/d/4/5d41402abc4b2a76b9719d911017c592.mp3 + * parts : [{"part":"int.","means":["哈喽,喂","你好,您好","表示问候","打招呼"]},{"part":"n.","means":["\u201c喂\u201d的招呼声或问候声"]},{"part":"vi.","means":["喊\u201c喂\u201d"]}] + */ + + private String ph_en; + private String ph_am; + private String ph_other; + private String ph_en_mp3; + private String ph_am_mp3; + private String ph_tts_mp3; + private List parts; + + public String getPh_en() { + return ph_en; + } + + public void setPh_en(String ph_en) { + this.ph_en = ph_en; + } + + public String getPh_am() { + return ph_am; + } + + public void setPh_am(String ph_am) { + this.ph_am = ph_am; + } + + public String getPh_other() { + return ph_other; + } + + public void setPh_other(String ph_other) { + this.ph_other = ph_other; + } + + public String getPh_en_mp3() { + return ph_en_mp3; + } + + public void setPh_en_mp3(String ph_en_mp3) { + this.ph_en_mp3 = ph_en_mp3; + } + + public String getPh_am_mp3() { + return ph_am_mp3; + } + + public void setPh_am_mp3(String ph_am_mp3) { + this.ph_am_mp3 = ph_am_mp3; + } + + public String getPh_tts_mp3() { + return ph_tts_mp3; + } + + public void setPh_tts_mp3(String ph_tts_mp3) { + this.ph_tts_mp3 = ph_tts_mp3; + } + + public List getParts() { + return parts; + } + + public void setParts(List parts) { + this.parts = parts; + } + + public static class PartsBean { + /** + * part : int. + * means : ["哈喽,喂","你好,您好","表示问候","打招呼"] + */ + + private String part; + private List means; + + public String getPart() { + return part; + } + + public void setPart(String part) { + this.part = part; + } + + public List getMeans() { + return means; + } + + public void setMeans(List means) { + this.means = means; + } + } + } +} diff --git a/fBReader/src/main/java/dyg/beans/GsonBuildList.java b/fBReader/src/main/java/dyg/beans/GsonBuildList.java new file mode 100644 index 0000000..eb2ef71 --- /dev/null +++ b/fBReader/src/main/java/dyg/beans/GsonBuildList.java @@ -0,0 +1,120 @@ +package dyg.beans; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.JsonSyntaxException; + +import java.lang.reflect.Type; + +public class GsonBuildList { + public static Gson normalGson = new Gson(); + + static class ExchangeBeanSerializer implements JsonDeserializer { + @Override + public CiBaWordBeanJson.ExchangeBean deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + return null; + } + } + +// static class SymbolSerializer implements JsonDeserializer { +// @Override +// public CiBaWordBeanJson.SymbolsBean deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { +// +// return null; +// } +// } + + public static Gson buildGson() { + Gson gson = null; + gson = new GsonBuilder() + .registerTypeAdapter(Integer.class, new IntegerDefault0Adapter()) + .registerTypeAdapter(int.class, new IntegerDefault0Adapter()) + .registerTypeAdapter(Double.class, new DoubleDefault0Adapter()) + .registerTypeAdapter(double.class, new DoubleDefault0Adapter()) + .registerTypeAdapter(Long.class, new LongDefault0Adapter()) + .registerTypeAdapter(long.class, new LongDefault0Adapter()) + .registerTypeAdapter(CiBaWordBeanJson.ExchangeBean.class, new ExchangeBeanSerializer()) +// .registerTypeAdapter(CiBaWordBeanJson.SymbolsBean.class, new SymbolSerializer()) + .create(); + + return gson; + } + + static class LongDefault0Adapter implements JsonSerializer, JsonDeserializer { + @Override + public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + try { + if (json.getAsString().equals("") || json.getAsString().equals("null")) {//定义为long类型,如果后台返回""或者null,则返回0 + return 0l; + } + } catch (Exception ignore) { + } + try { + return json.getAsLong(); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + } + + + @Override + public JsonElement serialize(Long src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src); + } + } + + static class DoubleDefault0Adapter implements JsonSerializer, JsonDeserializer { + @Override + public Double deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + try { + if (json.getAsString().equals("") || json.getAsString().equals("null")) {//定义为double类型,如果后台返回""或者null,则返回0.00 + return 0.00; + } + } catch (Exception ignore) { + } + try { + return json.getAsDouble(); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + } + + @Override + public JsonElement serialize(Double src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src); + } + } + + + static class IntegerDefault0Adapter implements JsonSerializer, JsonDeserializer { + @Override + public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + try { + if (json.getAsString().equals("") || json.getAsString().equals("null")) {//定义为int类型,如果后台返回""或者null,则返回0 + return 0; + } + } catch (Exception ignore) { + } + try { + return json.getAsInt(); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + } + + @Override + public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src); + } + } + +} diff --git a/fBReader/src/main/java/dyg/net/LoveFamousBookExecutor.java b/fBReader/src/main/java/dyg/net/LoveFamousBookExecutor.java new file mode 100644 index 0000000..01a842a --- /dev/null +++ b/fBReader/src/main/java/dyg/net/LoveFamousBookExecutor.java @@ -0,0 +1,26 @@ +package dyg.net; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class LoveFamousBookExecutor { + + private ExecutorService executors = null; + + private LoveFamousBookExecutor() { + executors = Executors.newFixedThreadPool(4); + } + + public LoveFamousBookExecutor getInstance() { + return Singleton.executor; + } + + private static class Singleton { + private static final LoveFamousBookExecutor executor = new LoveFamousBookExecutor(); + } + + public void exec(Runnable run) { + executors.submit(run); + } + +} diff --git a/fBReader/src/main/java/dyg/net/LoveFamousBookNet.java b/fBReader/src/main/java/dyg/net/LoveFamousBookNet.java new file mode 100644 index 0000000..dfb5c0c --- /dev/null +++ b/fBReader/src/main/java/dyg/net/LoveFamousBookNet.java @@ -0,0 +1,14 @@ +package dyg.net; + +import java.util.HashMap; + +import dyg.beans.CiBaWordBeanJson; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.QueryMap; + +public interface LoveFamousBookNet { + @GET("/api/dictionary.php") + Call getWords(@QueryMap HashMap map); + +} diff --git a/fBReader/src/main/java/dyg/net/LoveFamousMp3FileDownload.java b/fBReader/src/main/java/dyg/net/LoveFamousMp3FileDownload.java new file mode 100644 index 0000000..4360487 --- /dev/null +++ b/fBReader/src/main/java/dyg/net/LoveFamousMp3FileDownload.java @@ -0,0 +1,11 @@ +package dyg.net; + +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Url; + +public interface LoveFamousMp3FileDownload { + @GET + Call downloadMp3(@Url String url); +} diff --git a/fBReader/src/main/java/org/geometerplus/android/fbreader/FBReader.java b/fBReader/src/main/java/org/geometerplus/android/fbreader/FBReader.java index d6c5cca..95ae61c 100644 --- a/fBReader/src/main/java/org/geometerplus/android/fbreader/FBReader.java +++ b/fBReader/src/main/java/org/geometerplus/android/fbreader/FBReader.java @@ -19,30 +19,64 @@ package org.geometerplus.android.fbreader; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.*; - +import android.Manifest; import android.annotation.TargetApi; -import android.app.Activity; import android.app.SearchManager; -import android.content.*; -import android.graphics.Color; +import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; import android.net.Uri; -import android.os.*; -import android.view.*; +import android.os.Build; +import android.os.Bundle; +import android.os.PowerManager; +import android.support.annotation.NonNull; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.Window; +import android.view.WindowManager; import android.widget.RelativeLayout; +import org.geometerplus.android.fbreader.api.ApiListener; +import org.geometerplus.android.fbreader.api.ApiServerImplementation; +import org.geometerplus.android.fbreader.api.FBReaderIntents; +import org.geometerplus.android.fbreader.api.MenuNode; +import org.geometerplus.android.fbreader.api.PluginApi; +import org.geometerplus.android.fbreader.dict.DictionaryUtil; +import org.geometerplus.android.fbreader.formatPlugin.PluginUtil; +import org.geometerplus.android.fbreader.httpd.DataService; +import org.geometerplus.android.fbreader.libraryService.BookCollectionShadow; +import org.geometerplus.android.fbreader.sync.SyncOperations; +import org.geometerplus.android.fbreader.tips.TipsActivity; +import org.geometerplus.android.util.DeviceType; +import org.geometerplus.android.util.SearchDialogUtil; +import org.geometerplus.android.util.UIMessageUtil; +import org.geometerplus.android.util.UIUtil; +import org.geometerplus.fbreader.Paths; +import org.geometerplus.fbreader.book.Book; +import org.geometerplus.fbreader.book.BookUtil; +import org.geometerplus.fbreader.book.Bookmark; +import org.geometerplus.fbreader.bookmodel.BookModel; +import org.geometerplus.fbreader.fbreader.ActionCode; +import org.geometerplus.fbreader.fbreader.DictionaryHighlighting; +import org.geometerplus.fbreader.fbreader.FBReaderApp; +import org.geometerplus.fbreader.fbreader.options.CancelMenuHelper; +import org.geometerplus.fbreader.fbreader.options.ColorProfile; +import org.geometerplus.fbreader.formats.ExternalFormatPlugin; +import org.geometerplus.fbreader.formats.PluginCollection; +import org.geometerplus.fbreader.tips.TipsManager; import org.geometerplus.zlibrary.core.application.ZLApplicationWindow; import org.geometerplus.zlibrary.core.filesystem.ZLFile; import org.geometerplus.zlibrary.core.library.ZLibrary; import org.geometerplus.zlibrary.core.options.Config; import org.geometerplus.zlibrary.core.resources.ZLResource; import org.geometerplus.zlibrary.core.view.ZLViewWidget; - import org.geometerplus.zlibrary.text.view.ZLTextRegion; import org.geometerplus.zlibrary.text.view.ZLTextView; - import org.geometerplus.zlibrary.ui.android.R; import org.geometerplus.zlibrary.ui.android.error.ErrorKeys; import org.geometerplus.zlibrary.ui.android.library.ZLAndroidApplication; @@ -50,959 +84,976 @@ import org.geometerplus.zlibrary.ui.android.view.AndroidFontUtil; import org.geometerplus.zlibrary.ui.android.view.ZLAndroidWidget; -import org.geometerplus.fbreader.Paths; -import org.geometerplus.fbreader.book.*; -import org.geometerplus.fbreader.bookmodel.BookModel; -import org.geometerplus.fbreader.fbreader.*; -import org.geometerplus.fbreader.fbreader.options.CancelMenuHelper; -import org.geometerplus.fbreader.fbreader.options.ColorProfile; -import org.geometerplus.fbreader.formats.ExternalFormatPlugin; -import org.geometerplus.fbreader.formats.PluginCollection; -import org.geometerplus.fbreader.tips.TipsManager; - -import org.geometerplus.android.fbreader.api.*; -import org.geometerplus.android.fbreader.dict.DictionaryUtil; -import org.geometerplus.android.fbreader.formatPlugin.PluginUtil; -import org.geometerplus.android.fbreader.httpd.DataService; -import org.geometerplus.android.fbreader.libraryService.BookCollectionShadow; -import org.geometerplus.android.fbreader.sync.SyncOperations; -import org.geometerplus.android.fbreader.tips.TipsActivity; - -import org.geometerplus.android.util.*; - -public final class FBReader extends FBReaderMainActivity implements ZLApplicationWindow { - public static final int RESULT_DO_NOTHING = RESULT_FIRST_USER; - public static final int RESULT_REPAINT = RESULT_FIRST_USER + 1; - - public static Intent defaultIntent(Context context) { - return new Intent(context, FBReader.class) - .setAction(FBReaderIntents.Action.VIEW) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - } - - public static void openBookActivity(Context context, Book book, Bookmark bookmark) { - final Intent intent = defaultIntent(context); - FBReaderIntents.putBookExtra(intent, book); - FBReaderIntents.putBookmarkExtra(intent, bookmark); - context.startActivity(intent); - } - - private FBReaderApp myFBReaderApp; - private volatile Book myBook; - - private RelativeLayout myRootView; - private ZLAndroidWidget myMainView; - - private volatile boolean myShowStatusBarFlag; - private String myMenuLanguage; - - final DataService.Connection DataConnection = new DataService.Connection(); - - volatile boolean IsPaused = false; - private volatile long myResumeTimestamp; - volatile Runnable OnResumeAction = null; - - private Intent myCancelIntent = null; - private Intent myOpenBookIntent = null; - - private final FBReaderApp.Notifier myNotifier = new AppNotifier(this); - - private static final String PLUGIN_ACTION_PREFIX = "___"; - private final List myPluginActions = - new LinkedList(); - private final BroadcastReceiver myPluginInfoReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final ArrayList actions = getResultExtras(true).getParcelableArrayList(PluginApi.PluginInfo.KEY); - if (actions != null) { - synchronized (myPluginActions) { - int index = 0; - while (index < myPluginActions.size()) { - myFBReaderApp.removeAction(PLUGIN_ACTION_PREFIX + index++); - } - myPluginActions.addAll(actions); - index = 0; - for (PluginApi.ActionInfo info : myPluginActions) { - myFBReaderApp.addAction( - PLUGIN_ACTION_PREFIX + index++, - new RunPluginAction(FBReader.this, myFBReaderApp, info.getId()) - ); - } - } - } - } - }; - - private synchronized void openBook(Intent intent, final Runnable action, boolean force) { - if (!force && myBook != null) { - return; - } - - myBook = FBReaderIntents.getBookExtra(intent, myFBReaderApp.Collection); - final Bookmark bookmark = FBReaderIntents.getBookmarkExtra(intent); - if (myBook == null) { - final Uri data = intent.getData(); - if (data != null) { - myBook = createBookForFile(ZLFile.createFileByPath(data.getPath())); - } - } - if (myBook != null) { - ZLFile file = BookUtil.fileByBook(myBook); - if (!file.exists()) { - if (file.getPhysicalFile() != null) { - file = file.getPhysicalFile(); - } - UIMessageUtil.showErrorMessage(this, "fileNotFound", file.getPath()); - myBook = null; - } else { - NotificationUtil.drop(this, myBook); - } - } - Config.Instance().runOnConnect(new Runnable() { - public void run() { - myFBReaderApp.openBook(myBook, bookmark, action, myNotifier); - AndroidFontUtil.clearFontCache(); - } - }); - } - - private Book createBookForFile(ZLFile file) { - if (file == null) { - return null; - } - Book book = myFBReaderApp.Collection.getBookByFile(file.getPath()); - if (book != null) { - return book; - } - if (file.isArchive()) { - for (ZLFile child : file.children()) { - book = myFBReaderApp.Collection.getBookByFile(child.getPath()); - if (book != null) { - return book; - } - } - } - return null; - } - - private Runnable getPostponedInitAction() { - return new Runnable() { - public void run() { - runOnUiThread(new Runnable() { - public void run() { - new TipRunner().start(); - DictionaryUtil.init(FBReader.this, null); - final Intent intent = getIntent(); - if (intent != null && FBReaderIntents.Action.PLUGIN.equals(intent.getAction())) { - new RunPluginAction(FBReader.this, myFBReaderApp, intent.getData()).run(); - } - } - }); - } - }; - } - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - bindService( - new Intent(this, DataService.class), - DataConnection, - DataService.BIND_AUTO_CREATE - ); - - final Config config = Config.Instance(); - config.runOnConnect(new Runnable() { - public void run() { - config.requestAllValuesForGroup("Options"); - config.requestAllValuesForGroup("Style"); - config.requestAllValuesForGroup("LookNFeel"); - config.requestAllValuesForGroup("Fonts"); - config.requestAllValuesForGroup("Colors"); - config.requestAllValuesForGroup("Files"); - } - }); - - final ZLAndroidLibrary zlibrary = getZLibrary(); - myShowStatusBarFlag = zlibrary.ShowStatusBarOption.getValue(); - - requestWindowFeature(Window.FEATURE_NO_TITLE); - setContentView(R.layout.main); - myRootView = (RelativeLayout)findViewById(R.id.root_view); - myMainView = (ZLAndroidWidget)findViewById(R.id.main_view); - setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); - - myFBReaderApp = (FBReaderApp)FBReaderApp.Instance(); - if (myFBReaderApp == null) { - myFBReaderApp = new FBReaderApp(Paths.systemInfo(this), new BookCollectionShadow()); - } - getCollection().bindToService(this, null); - myBook = null; - - myFBReaderApp.setWindow(this); - myFBReaderApp.initWindow(); - - myFBReaderApp.setExternalFileOpener(new ExternalFileOpener(this)); - - getWindow().setFlags( - WindowManager.LayoutParams.FLAG_FULLSCREEN, - myShowStatusBarFlag ? 0 : WindowManager.LayoutParams.FLAG_FULLSCREEN - ); - - if (myFBReaderApp.getPopupById(TextSearchPopup.ID) == null) { - new TextSearchPopup(myFBReaderApp); - } - if (myFBReaderApp.getPopupById(NavigationPopup.ID) == null) { - new NavigationPopup(myFBReaderApp); - } - if (myFBReaderApp.getPopupById(SelectionPopup.ID) == null) { - new SelectionPopup(myFBReaderApp); - } - - myFBReaderApp.addAction(ActionCode.SHOW_LIBRARY, new ShowLibraryAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SHOW_PREFERENCES, new ShowPreferencesAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SHOW_BOOK_INFO, new ShowBookInfoAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SHOW_TOC, new ShowTOCAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SHOW_BOOKMARKS, new ShowBookmarksAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SHOW_NETWORK_LIBRARY, new ShowNetworkLibraryAction(this, myFBReaderApp)); - - myFBReaderApp.addAction(ActionCode.SHOW_MENU, new ShowMenuAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SHOW_NAVIGATION, new ShowNavigationAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SEARCH, new SearchAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SHARE_BOOK, new ShareBookAction(this, myFBReaderApp)); - - myFBReaderApp.addAction(ActionCode.SELECTION_SHOW_PANEL, new SelectionShowPanelAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SELECTION_HIDE_PANEL, new SelectionHidePanelAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SELECTION_COPY_TO_CLIPBOARD, new SelectionCopyAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SELECTION_SHARE, new SelectionShareAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SELECTION_TRANSLATE, new SelectionTranslateAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.SELECTION_BOOKMARK, new SelectionBookmarkAction(this, myFBReaderApp)); - - myFBReaderApp.addAction(ActionCode.DISPLAY_BOOK_POPUP, new DisplayBookPopupAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.PROCESS_HYPERLINK, new ProcessHyperlinkAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.OPEN_VIDEO, new OpenVideoAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.HIDE_TOAST, new HideToastAction(this, myFBReaderApp)); - - myFBReaderApp.addAction(ActionCode.SHOW_CANCEL_MENU, new ShowCancelMenuAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.OPEN_START_SCREEN, new StartScreenAction(this, myFBReaderApp)); - - myFBReaderApp.addAction(ActionCode.SET_SCREEN_ORIENTATION_SYSTEM, new SetScreenOrientationAction(this, myFBReaderApp, ZLibrary.SCREEN_ORIENTATION_SYSTEM)); - myFBReaderApp.addAction(ActionCode.SET_SCREEN_ORIENTATION_SENSOR, new SetScreenOrientationAction(this, myFBReaderApp, ZLibrary.SCREEN_ORIENTATION_SENSOR)); - myFBReaderApp.addAction(ActionCode.SET_SCREEN_ORIENTATION_PORTRAIT, new SetScreenOrientationAction(this, myFBReaderApp, ZLibrary.SCREEN_ORIENTATION_PORTRAIT)); - myFBReaderApp.addAction(ActionCode.SET_SCREEN_ORIENTATION_LANDSCAPE, new SetScreenOrientationAction(this, myFBReaderApp, ZLibrary.SCREEN_ORIENTATION_LANDSCAPE)); - if (getZLibrary().supportsAllOrientations()) { - myFBReaderApp.addAction(ActionCode.SET_SCREEN_ORIENTATION_REVERSE_PORTRAIT, new SetScreenOrientationAction(this, myFBReaderApp, ZLibrary.SCREEN_ORIENTATION_REVERSE_PORTRAIT)); - myFBReaderApp.addAction(ActionCode.SET_SCREEN_ORIENTATION_REVERSE_LANDSCAPE, new SetScreenOrientationAction(this, myFBReaderApp, ZLibrary.SCREEN_ORIENTATION_REVERSE_LANDSCAPE)); - } - myFBReaderApp.addAction(ActionCode.OPEN_WEB_HELP, new OpenWebHelpAction(this, myFBReaderApp)); - myFBReaderApp.addAction(ActionCode.INSTALL_PLUGINS, new InstallPluginsAction(this, myFBReaderApp)); - - myFBReaderApp.addAction(ActionCode.SWITCH_TO_DAY_PROFILE, new SwitchProfileAction(this, myFBReaderApp, ColorProfile.DAY)); - myFBReaderApp.addAction(ActionCode.SWITCH_TO_NIGHT_PROFILE, new SwitchProfileAction(this, myFBReaderApp, ColorProfile.NIGHT)); - - final Intent intent = getIntent(); - final String action = intent.getAction(); - - myOpenBookIntent = intent; - if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) { - if (FBReaderIntents.Action.CLOSE.equals(action)) { - myCancelIntent = intent; - myOpenBookIntent = null; - } else if (FBReaderIntents.Action.PLUGIN_CRASH.equals(action)) { - myFBReaderApp.ExternalBook = null; - myOpenBookIntent = null; - getCollection().bindToService(this, new Runnable() { - public void run() { - myFBReaderApp.openBook(null, null, null, myNotifier); - } - }); - } - } - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - setStatusBarVisibility(true); - setupMenu(menu); - - return super.onPrepareOptionsMenu(menu); - } - - @Override - public void onOptionsMenuClosed(Menu menu) { - super.onOptionsMenuClosed(menu); - setStatusBarVisibility(false); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - setStatusBarVisibility(false); - return super.onOptionsItemSelected(item); - } - - @Override - protected void onNewIntent(final Intent intent) { - final String action = intent.getAction(); - final Uri data = intent.getData(); - - if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { - super.onNewIntent(intent); - } else if (Intent.ACTION_VIEW.equals(action) - && data != null && "fbreader-action".equals(data.getScheme())) { - myFBReaderApp.runAction(data.getEncodedSchemeSpecificPart(), data.getFragment()); - } else if (Intent.ACTION_VIEW.equals(action) || FBReaderIntents.Action.VIEW.equals(action)) { - myOpenBookIntent = intent; - if (myFBReaderApp.Model == null && myFBReaderApp.ExternalBook != null) { - final BookCollectionShadow collection = getCollection(); - final Book b = FBReaderIntents.getBookExtra(intent, collection); - if (!collection.sameBook(b, myFBReaderApp.ExternalBook)) { - try { - final ExternalFormatPlugin plugin = - (ExternalFormatPlugin)BookUtil.getPlugin( - PluginCollection.Instance(Paths.systemInfo(this)), - myFBReaderApp.ExternalBook - ); - startActivity(PluginUtil.createIntent(plugin, FBReaderIntents.Action.PLUGIN_KILL)); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } else if (FBReaderIntents.Action.PLUGIN.equals(action)) { - new RunPluginAction(this, myFBReaderApp, data).run(); - } else if (Intent.ACTION_SEARCH.equals(action)) { - final String pattern = intent.getStringExtra(SearchManager.QUERY); - final Runnable runnable = new Runnable() { - public void run() { - final TextSearchPopup popup = (TextSearchPopup)myFBReaderApp.getPopupById(TextSearchPopup.ID); - popup.initPosition(); - myFBReaderApp.MiscOptions.TextSearchPattern.setValue(pattern); - if (myFBReaderApp.getTextView().search(pattern, true, false, false, false) != 0) { - runOnUiThread(new Runnable() { - public void run() { - myFBReaderApp.showPopup(popup.getId()); - } - }); - } else { - runOnUiThread(new Runnable() { - public void run() { - UIMessageUtil.showErrorMessage(FBReader.this, "textNotFound"); - popup.StartPosition = null; - } - }); - } - } - }; - UIUtil.wait("search", runnable, this); - } else if (FBReaderIntents.Action.CLOSE.equals(intent.getAction())) { - myCancelIntent = intent; - myOpenBookIntent = null; - } else if (FBReaderIntents.Action.PLUGIN_CRASH.equals(intent.getAction())) { - final Book book = FBReaderIntents.getBookExtra(intent, myFBReaderApp.Collection); - myFBReaderApp.ExternalBook = null; - myOpenBookIntent = null; - getCollection().bindToService(this, new Runnable() { - public void run() { - final BookCollectionShadow collection = getCollection(); - Book b = collection.getRecentBook(0); - if (collection.sameBook(b, book)) { - b = collection.getRecentBook(1); - } - myFBReaderApp.openBook(b, null, null, myNotifier); - } - }); - } else { - super.onNewIntent(intent); - } - } - - @Override - protected void onStart() { - super.onStart(); - - getCollection().bindToService(this, new Runnable() { - public void run() { - new Thread() { - public void run() { - getPostponedInitAction().run(); - } - }.start(); - - myFBReaderApp.getViewWidget().repaint(); - } - }); - - initPluginActions(); - - final ZLAndroidLibrary zlibrary = getZLibrary(); - - Config.Instance().runOnConnect(new Runnable() { - public void run() { - final boolean showStatusBar = zlibrary.ShowStatusBarOption.getValue(); - if (showStatusBar != myShowStatusBarFlag) { - finish(); - startActivity(new Intent(FBReader.this, FBReader.class)); - } - zlibrary.ShowStatusBarOption.saveSpecialValue(); - myFBReaderApp.ViewOptions.ColorProfileName.saveSpecialValue(); - SetScreenOrientationAction.setOrientation(FBReader.this, zlibrary.getOrientationOption().getValue()); - } - }); - - ((PopupPanel)myFBReaderApp.getPopupById(TextSearchPopup.ID)).setPanelInfo(this, myRootView); - ((NavigationPopup)myFBReaderApp.getPopupById(NavigationPopup.ID)).setPanelInfo(this, myRootView); - ((PopupPanel)myFBReaderApp.getPopupById(SelectionPopup.ID)).setPanelInfo(this, myRootView); - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - switchWakeLock(hasFocus && - getZLibrary().BatteryLevelToTurnScreenOffOption.getValue() < - myFBReaderApp.getBatteryLevel() - ); - } - - private void initPluginActions() { - synchronized (myPluginActions) { - int index = 0; - while (index < myPluginActions.size()) { - myFBReaderApp.removeAction(PLUGIN_ACTION_PREFIX + index++); - } - myPluginActions.clear(); - } - - sendOrderedBroadcast( - new Intent(PluginApi.ACTION_REGISTER), - null, - myPluginInfoReceiver, - null, - RESULT_OK, - null, - null - ); - } - - private class TipRunner extends Thread { - TipRunner() { - setPriority(MIN_PRIORITY); - } - - public void run() { - final TipsManager manager = new TipsManager(Paths.systemInfo(FBReader.this)); - switch (manager.requiredAction()) { - case Initialize: - startActivity(new Intent( - TipsActivity.INITIALIZE_ACTION, null, FBReader.this, TipsActivity.class - )); - break; - case Show: - startActivity(new Intent( - TipsActivity.SHOW_TIP_ACTION, null, FBReader.this, TipsActivity.class - )); - break; - case Download: - manager.startDownloading(); - break; - case None: - break; - } - } - } - - @Override - protected void onResume() { - super.onResume(); - - myStartTimer = true; - Config.Instance().runOnConnect(new Runnable() { - public void run() { - SyncOperations.enableSync(FBReader.this, myFBReaderApp.SyncOptions); - - final int brightnessLevel = - getZLibrary().ScreenBrightnessLevelOption.getValue(); - if (brightnessLevel != 0) { - getViewWidget().setScreenBrightness(brightnessLevel); - } else { - setScreenBrightnessAuto(); - } - if (getZLibrary().DisableButtonLightsOption.getValue()) { - setButtonLight(false); - } - - getCollection().bindToService(FBReader.this, new Runnable() { - public void run() { - final BookModel model = myFBReaderApp.Model; - if (model == null || model.Book == null) { - return; - } - onPreferencesUpdate(myFBReaderApp.Collection.getBookById(model.Book.getId())); - } - }); - } - }); - - registerReceiver(myBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - IsPaused = false; - myResumeTimestamp = System.currentTimeMillis(); - if (OnResumeAction != null) { - final Runnable action = OnResumeAction; - OnResumeAction = null; - action.run(); - } - - registerReceiver(mySyncUpdateReceiver, new IntentFilter(FBReaderIntents.Event.SYNC_UPDATED)); - - SetScreenOrientationAction.setOrientation(this, getZLibrary().getOrientationOption().getValue()); - if (myCancelIntent != null) { - final Intent intent = myCancelIntent; - myCancelIntent = null; - getCollection().bindToService(this, new Runnable() { - public void run() { - runCancelAction(intent); - } - }); - return; - } else if (myOpenBookIntent != null) { - final Intent intent = myOpenBookIntent; - myOpenBookIntent = null; - getCollection().bindToService(this, new Runnable() { - public void run() { - openBook(intent, null, true); - } - }); - } else if (myFBReaderApp.getCurrentServerBook(null) != null) { - getCollection().bindToService(this, new Runnable() { - public void run() { - myFBReaderApp.useSyncInfo(true, myNotifier); - } - }); - } else if (myFBReaderApp.Model == null && myFBReaderApp.ExternalBook != null) { - getCollection().bindToService(this, new Runnable() { - public void run() { - myFBReaderApp.openBook(myFBReaderApp.ExternalBook, null, null, myNotifier); - } - }); - } else { - getCollection().bindToService(this, new Runnable() { - public void run() { - myFBReaderApp.useSyncInfo(true, myNotifier); - } - }); - } - - PopupPanel.restoreVisibilities(myFBReaderApp); - ApiServerImplementation.sendEvent(this, ApiListener.EVENT_READ_MODE_OPENED); - } - - @Override - protected void onPause() { - SyncOperations.quickSync(this, myFBReaderApp.SyncOptions); - - IsPaused = true; - try { - unregisterReceiver(mySyncUpdateReceiver); - } catch (IllegalArgumentException e) { - } - - try { - unregisterReceiver(myBatteryInfoReceiver); - } catch (IllegalArgumentException e) { - // do nothing, this exception means that myBatteryInfoReceiver was not registered - } - - myFBReaderApp.stopTimer(); - if (getZLibrary().DisableButtonLightsOption.getValue()) { - setButtonLight(true); - } - myFBReaderApp.onWindowClosing(); - - super.onPause(); - } - - @Override - protected void onStop() { - ApiServerImplementation.sendEvent(this, ApiListener.EVENT_READ_MODE_CLOSED); - PopupPanel.removeAllWindows(myFBReaderApp, this); - super.onStop(); - } - - @Override - protected void onDestroy() { - getCollection().unbind(); - unbindService(DataConnection); - super.onDestroy(); - } - - @Override - public void onLowMemory() { - myFBReaderApp.onWindowClosing(); - super.onLowMemory(); - } - - @Override - public boolean onSearchRequested() { - final FBReaderApp.PopupPanel popup = myFBReaderApp.getActivePopup(); - myFBReaderApp.hideActivePopup(); - if (DeviceType.Instance().hasStandardSearchDialog()) { - final SearchManager manager = (SearchManager)getSystemService(SEARCH_SERVICE); - manager.setOnCancelListener(new SearchManager.OnCancelListener() { - public void onCancel() { - if (popup != null) { - myFBReaderApp.showPopup(popup.getId()); - } - manager.setOnCancelListener(null); - } - }); - startSearch(myFBReaderApp.MiscOptions.TextSearchPattern.getValue(), true, null, false); - } else { - SearchDialogUtil.showDialog( - this, FBReader.class, myFBReaderApp.MiscOptions.TextSearchPattern.getValue(), new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface di) { - if (popup != null) { - myFBReaderApp.showPopup(popup.getId()); - } - } - } - ); - } - return true; - } - - public void showSelectionPanel() { - final ZLTextView view = myFBReaderApp.getTextView(); - ((SelectionPopup)myFBReaderApp.getPopupById(SelectionPopup.ID)) - .move(view.getSelectionStartY(), view.getSelectionEndY()); - myFBReaderApp.showPopup(SelectionPopup.ID); - } - - public void hideSelectionPanel() { - final FBReaderApp.PopupPanel popup = myFBReaderApp.getActivePopup(); - if (popup != null && popup.getId() == SelectionPopup.ID) { - myFBReaderApp.hideActivePopup(); - } - } - - private void onPreferencesUpdate(Book book) { - AndroidFontUtil.clearFontCache(); - myFBReaderApp.onBookUpdated(book); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - default: - super.onActivityResult(requestCode, resultCode, data); - break; - case REQUEST_PREFERENCES: - if (resultCode != RESULT_DO_NOTHING && data != null) { - final Book book = FBReaderIntents.getBookExtra(data, myFBReaderApp.Collection); - if (book != null) { - getCollection().bindToService(this, new Runnable() { - public void run() { - onPreferencesUpdate(book); - } - }); - } - } - break; - case REQUEST_CANCEL_MENU: - runCancelAction(data); - break; - } - } - - private void runCancelAction(Intent intent) { - final CancelMenuHelper.ActionType type; - try { - type = CancelMenuHelper.ActionType.valueOf( - intent.getStringExtra(FBReaderIntents.Key.TYPE) - ); - } catch (Exception e) { - // invalid (or null) type value - return; - } - Bookmark bookmark = null; - if (type == CancelMenuHelper.ActionType.returnTo) { - bookmark = FBReaderIntents.getBookmarkExtra(intent); - if (bookmark == null) { - return; - } - } - myFBReaderApp.runCancelAction(type, bookmark); - } - - public void navigate() { - ((NavigationPopup)myFBReaderApp.getPopupById(NavigationPopup.ID)).runNavigation(); - } - - private Menu addSubmenu(Menu menu, String id) { - return menu.addSubMenu(ZLResource.resource("menu").getResource(id).getValue()); - } - - private void addMenuItem(Menu menu, String actionId, Integer iconId, String name) { - if (name == null) { - name = ZLResource.resource("menu").getResource(actionId).getValue(); - } - final MenuItem menuItem = menu.add(name); - if (iconId != null) { - menuItem.setIcon(iconId); - } - menuItem.setOnMenuItemClickListener(myMenuListener); - myMenuItemMap.put(menuItem, actionId); - } - - private void addMenuItem(Menu menu, String actionId, String name) { - addMenuItem(menu, actionId, null, name); - } - - private void addMenuItem(Menu menu, String actionId, int iconId) { - addMenuItem(menu, actionId, iconId, null); - } - - private void addMenuItem(Menu menu, String actionId) { - addMenuItem(menu, actionId, null, null); - } - - private void fillMenu(Menu menu, List nodes) { - for (MenuNode n : nodes) { - if (n instanceof MenuNode.Item) { - final Integer iconId = ((MenuNode.Item)n).IconId; - if (iconId != null) { - addMenuItem(menu, n.Code, iconId); - } else { - addMenuItem(menu, n.Code); - } - } else /* if (n instanceof MenuNode.Submenu) */ { - final Menu submenu = addSubmenu(menu, n.Code); - fillMenu(submenu, ((MenuNode.Submenu)n).Children); - } - } - } - - private void setupMenu(Menu menu) { - final String menuLanguage = ZLResource.getLanguageOption().getValue(); - if (menuLanguage.equals(myMenuLanguage)) { - return; - } - myMenuLanguage = menuLanguage; - - menu.clear(); - fillMenu(menu, MenuData.topLevelNodes()); - synchronized (myPluginActions) { - int index = 0; - for (PluginApi.ActionInfo info : myPluginActions) { - if (info instanceof PluginApi.MenuActionInfo) { - addMenuItem( - menu, - PLUGIN_ACTION_PREFIX + index++, - ((PluginApi.MenuActionInfo)info).MenuItemName - ); - } - } - } - - refresh(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - - setupMenu(menu); - - return true; - } - - protected void onPluginNotFound(final Book book) { - final BookCollectionShadow collection = getCollection(); - collection.bindToService(this, new Runnable() { - public void run() { - final Book recent = collection.getRecentBook(0); - if (recent != null && !collection.sameBook(recent, book)) { - myFBReaderApp.openBook(recent, null, null, null); - } else { - myFBReaderApp.openHelpBook(); - } - } - }); - } - - private void setStatusBarVisibility(boolean visible) { - final ZLAndroidLibrary zlibrary = getZLibrary(); - if (DeviceType.Instance() != DeviceType.KINDLE_FIRE_1ST_GENERATION && !myShowStatusBarFlag) { - if (visible) { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); - } else { - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); - } - } - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return (myMainView != null && myMainView.onKeyDown(keyCode, event)) || super.onKeyDown(keyCode, event); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - return (myMainView != null && myMainView.onKeyUp(keyCode, event)) || super.onKeyUp(keyCode, event); - } - - private void setButtonLight(boolean enabled) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { - setButtonLightInternal(enabled); - } - } - - @TargetApi(Build.VERSION_CODES.FROYO) - private void setButtonLightInternal(boolean enabled) { - final WindowManager.LayoutParams attrs = getWindow().getAttributes(); - attrs.buttonBrightness = enabled ? -1.0f : 0.0f; - getWindow().setAttributes(attrs); - } - - private PowerManager.WakeLock myWakeLock; - private boolean myWakeLockToCreate; - private boolean myStartTimer; - - public final void createWakeLock() { - if (myWakeLockToCreate) { - synchronized (this) { - if (myWakeLockToCreate) { - myWakeLockToCreate = false; - myWakeLock = - ((PowerManager)getSystemService(POWER_SERVICE)) - .newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "FBReader"); - myWakeLock.acquire(); - } - } - } - if (myStartTimer) { - myFBReaderApp.startTimer(); - myStartTimer = false; - } - } - - private final void switchWakeLock(boolean on) { - if (on) { - if (myWakeLock == null) { - myWakeLockToCreate = true; - } - } else { - if (myWakeLock != null) { - synchronized (this) { - if (myWakeLock != null) { - myWakeLock.release(); - myWakeLock = null; - } - } - } - } - } - - private BroadcastReceiver myBatteryInfoReceiver = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - final int level = intent.getIntExtra("level", 100); - final ZLAndroidApplication application = (ZLAndroidApplication)getApplication(); - setBatteryLevel(level); - switchWakeLock( - hasWindowFocus() && - getZLibrary().BatteryLevelToTurnScreenOffOption.getValue() < level - ); - } - }; - - private BookCollectionShadow getCollection() { - return (BookCollectionShadow)myFBReaderApp.Collection; - } - - // methods from ZLApplicationWindow interface - @Override - public void showErrorMessage(String key) { - UIMessageUtil.showErrorMessage(this, key); - } - - @Override - public void showErrorMessage(String key, String parameter) { - UIMessageUtil.showErrorMessage(this, key, parameter); - } - - @Override - public FBReaderApp.SynchronousExecutor createExecutor(String key) { - return UIUtil.createExecutor(this, key); - } - - private int myBatteryLevel; - @Override - public int getBatteryLevel() { - return myBatteryLevel; - } - private void setBatteryLevel(int percent) { - myBatteryLevel = percent; - } - - @Override - public void close() { - finish(); - } - - @Override - public ZLViewWidget getViewWidget() { - return myMainView; - } - - private final HashMap myMenuItemMap = new HashMap(); - - private final MenuItem.OnMenuItemClickListener myMenuListener = - new MenuItem.OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - myFBReaderApp.runAction(myMenuItemMap.get(item)); - return true; - } - }; - - @Override - public void refresh() { - runOnUiThread(new Runnable() { - public void run() { - for (Map.Entry entry : myMenuItemMap.entrySet()) { - final String actionId = entry.getValue(); - final MenuItem menuItem = entry.getKey(); - menuItem.setVisible(myFBReaderApp.isActionVisible(actionId) && myFBReaderApp.isActionEnabled(actionId)); - switch (myFBReaderApp.isActionChecked(actionId)) { - case TRUE: - menuItem.setCheckable(true); - menuItem.setChecked(true); - break; - case FALSE: - menuItem.setCheckable(true); - menuItem.setChecked(false); - break; - case UNDEFINED: - menuItem.setCheckable(false); - break; - } - } - } - }); - } - - @Override - public void processException(Exception exception) { - exception.printStackTrace(); - - final Intent intent = new Intent( - FBReaderIntents.Action.ERROR, - new Uri.Builder().scheme(exception.getClass().getSimpleName()).build() - ); - intent.setPackage(FBReaderIntents.DEFAULT_PACKAGE); - intent.putExtra(ErrorKeys.MESSAGE, exception.getMessage()); - final StringWriter stackTrace = new StringWriter(); - exception.printStackTrace(new PrintWriter(stackTrace)); - intent.putExtra(ErrorKeys.STACKTRACE, stackTrace.toString()); +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import pub.devrel.easypermissions.AfterPermissionGranted; +import pub.devrel.easypermissions.EasyPermissions; + +public final class FBReader extends FBReaderMainActivity implements ZLApplicationWindow , EasyPermissions.PermissionCallbacks{ + private static final String TAG = "FBReader"; + public static final int RESULT_DO_NOTHING = RESULT_FIRST_USER; + public static final int RESULT_REPAINT = RESULT_FIRST_USER + 1; + private static final int RC_CAMERA_AND_LOCATION = 110; + + public static Intent defaultIntent(Context context) { + return new Intent(context, FBReader.class) + .setAction(FBReaderIntents.Action.VIEW) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + } + + public static void openBookActivity(Context context, Book book, Bookmark bookmark) { + final Intent intent = defaultIntent(context); + FBReaderIntents.putBookExtra(intent, book); + FBReaderIntents.putBookmarkExtra(intent, bookmark); + context.startActivity(intent); + } + + private FBReaderApp myFBReaderApp; + private volatile Book myBook; + + private RelativeLayout myRootView; + private ZLAndroidWidget myMainView; + + private volatile boolean myShowStatusBarFlag; + private String myMenuLanguage; + + final DataService.Connection DataConnection = new DataService.Connection(); + + volatile boolean IsPaused = false; + private volatile long myResumeTimestamp; + volatile Runnable OnResumeAction = null; + + private Intent myCancelIntent = null; + private Intent myOpenBookIntent = null; + + private final FBReaderApp.Notifier myNotifier = new AppNotifier(this); + + private static final String PLUGIN_ACTION_PREFIX = "___"; + private final List myPluginActions = + new LinkedList(); + private final BroadcastReceiver myPluginInfoReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final ArrayList actions = getResultExtras(true).getParcelableArrayList(PluginApi.PluginInfo.KEY); + if (actions != null) { + synchronized (myPluginActions) { + int index = 0; + while (index < myPluginActions.size()) { + myFBReaderApp.removeAction(PLUGIN_ACTION_PREFIX + index++); + } + myPluginActions.addAll(actions); + index = 0; + for (PluginApi.ActionInfo info : myPluginActions) { + myFBReaderApp.addAction( + PLUGIN_ACTION_PREFIX + index++, + new RunPluginAction(FBReader.this, myFBReaderApp, info.getId()) + ); + } + } + } + } + }; + + private synchronized void openBook(Intent intent, final Runnable action, boolean force) { + if (!force && myBook != null) { + return; + } + + myBook = FBReaderIntents.getBookExtra(intent, myFBReaderApp.Collection); + final Bookmark bookmark = FBReaderIntents.getBookmarkExtra(intent); + if (myBook == null) { + final Uri data = intent.getData(); + if (data != null) { + myBook = createBookForFile(ZLFile.createFileByPath(data.getPath())); + } + } + if (myBook != null) { + ZLFile file = BookUtil.fileByBook(myBook); + if (!file.exists()) { + if (file.getPhysicalFile() != null) { + file = file.getPhysicalFile(); + } + UIMessageUtil.showErrorMessage(this, "fileNotFound", file.getPath()); + myBook = null; + } else { + NotificationUtil.drop(this, myBook); + } + } + Config.Instance().runOnConnect(new Runnable() { + public void run() { + myFBReaderApp.openBook(myBook, bookmark, action, myNotifier); + AndroidFontUtil.clearFontCache(); + } + }); + } + + private Book createBookForFile(ZLFile file) { + if (file == null) { + return null; + } + Book book = myFBReaderApp.Collection.getBookByFile(file.getPath()); + if (book != null) { + return book; + } + if (file.isArchive()) { + for (ZLFile child : file.children()) { + book = myFBReaderApp.Collection.getBookByFile(child.getPath()); + if (book != null) { + return book; + } + } + } + return null; + } + + private Runnable getPostponedInitAction() { + return new Runnable() { + public void run() { + runOnUiThread(new Runnable() { + public void run() { + new TipRunner().start(); + DictionaryUtil.init(FBReader.this, null); + final Intent intent = getIntent(); + if (intent != null && FBReaderIntents.Action.PLUGIN.equals(intent.getAction())) { + new RunPluginAction(FBReader.this, myFBReaderApp, intent.getData()).run(); + } + } + }); + } + }; + } + @AfterPermissionGranted(RC_CAMERA_AND_LOCATION) + private void methodRequiresTwoPermission() { + String[] perms = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}; + if (EasyPermissions.hasPermissions(this, perms)) { + // Already have permission, do the thing + // ... + } else { + // Do not have permissions, request them now + EasyPermissions.requestPermissions(this, "需要sdcard读写权限", + RC_CAMERA_AND_LOCATION, perms); + } + } + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + methodRequiresTwoPermission(); + bindService( + new Intent(this, DataService.class), + DataConnection, + DataService.BIND_AUTO_CREATE + ); + + final Config config = Config.Instance(); + config.runOnConnect(new Runnable() { + public void run() { + config.requestAllValuesForGroup("Options"); + config.requestAllValuesForGroup("Style"); + config.requestAllValuesForGroup("LookNFeel"); + config.requestAllValuesForGroup("Fonts"); + config.requestAllValuesForGroup("Colors"); + config.requestAllValuesForGroup("Files"); + } + }); + + final ZLAndroidLibrary zlibrary = getZLibrary(); + myShowStatusBarFlag = zlibrary.ShowStatusBarOption.getValue(); + + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.main); + myRootView = (RelativeLayout) findViewById(R.id.root_view); + myMainView = (ZLAndroidWidget) findViewById(R.id.main_view); + setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); + + myFBReaderApp = (FBReaderApp) FBReaderApp.Instance(); + if (myFBReaderApp == null) { + myFBReaderApp = new FBReaderApp(Paths.systemInfo(this), new BookCollectionShadow()); + } + getCollection().bindToService(this, null); + myBook = null; + + myFBReaderApp.setWindow(this); + myFBReaderApp.initWindow(); + + myFBReaderApp.setExternalFileOpener(new ExternalFileOpener(this)); + + getWindow().setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + myShowStatusBarFlag ? 0 : WindowManager.LayoutParams.FLAG_FULLSCREEN + ); + + if (myFBReaderApp.getPopupById(TextSearchPopup.ID) == null) { + new TextSearchPopup(myFBReaderApp); + } + if (myFBReaderApp.getPopupById(NavigationPopup.ID) == null) { + new NavigationPopup(myFBReaderApp); + } + if (myFBReaderApp.getPopupById(SelectionPopup.ID) == null) { + new SelectionPopup(myFBReaderApp); + } + + myFBReaderApp.addAction(ActionCode.SHOW_LIBRARY, new ShowLibraryAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SHOW_PREFERENCES, new ShowPreferencesAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SHOW_BOOK_INFO, new ShowBookInfoAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SHOW_TOC, new ShowTOCAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SHOW_BOOKMARKS, new ShowBookmarksAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SHOW_NETWORK_LIBRARY, new ShowNetworkLibraryAction(this, myFBReaderApp)); + + myFBReaderApp.addAction(ActionCode.SHOW_MENU, new ShowMenuAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SHOW_TRANSLATE, new TranslateAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SHOW_NAVIGATION, new ShowNavigationAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SEARCH, new SearchAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SHARE_BOOK, new ShareBookAction(this, myFBReaderApp)); + + myFBReaderApp.addAction(ActionCode.SELECTION_SHOW_PANEL, new SelectionShowPanelAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SELECTION_HIDE_PANEL, new SelectionHidePanelAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SELECTION_COPY_TO_CLIPBOARD, new SelectionCopyAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SELECTION_SHARE, new SelectionShareAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SELECTION_TRANSLATE, new SelectionTranslateAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.SELECTION_BOOKMARK, new SelectionBookmarkAction(this, myFBReaderApp)); + + myFBReaderApp.addAction(ActionCode.DISPLAY_BOOK_POPUP, new DisplayBookPopupAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.PROCESS_HYPERLINK, new ProcessHyperlinkAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.OPEN_VIDEO, new OpenVideoAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.HIDE_TOAST, new HideToastAction(this, myFBReaderApp)); + + myFBReaderApp.addAction(ActionCode.SHOW_CANCEL_MENU, new ShowCancelMenuAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.OPEN_START_SCREEN, new StartScreenAction(this, myFBReaderApp)); + + myFBReaderApp.addAction(ActionCode.SET_SCREEN_ORIENTATION_SYSTEM, new SetScreenOrientationAction(this, myFBReaderApp, ZLibrary.SCREEN_ORIENTATION_SYSTEM)); + myFBReaderApp.addAction(ActionCode.SET_SCREEN_ORIENTATION_SENSOR, new SetScreenOrientationAction(this, myFBReaderApp, ZLibrary.SCREEN_ORIENTATION_SENSOR)); + myFBReaderApp.addAction(ActionCode.SET_SCREEN_ORIENTATION_PORTRAIT, new SetScreenOrientationAction(this, myFBReaderApp, ZLibrary.SCREEN_ORIENTATION_PORTRAIT)); + myFBReaderApp.addAction(ActionCode.SET_SCREEN_ORIENTATION_LANDSCAPE, new SetScreenOrientationAction(this, myFBReaderApp, ZLibrary.SCREEN_ORIENTATION_LANDSCAPE)); + if (getZLibrary().supportsAllOrientations()) { + myFBReaderApp.addAction(ActionCode.SET_SCREEN_ORIENTATION_REVERSE_PORTRAIT, new SetScreenOrientationAction(this, myFBReaderApp, ZLibrary.SCREEN_ORIENTATION_REVERSE_PORTRAIT)); + myFBReaderApp.addAction(ActionCode.SET_SCREEN_ORIENTATION_REVERSE_LANDSCAPE, new SetScreenOrientationAction(this, myFBReaderApp, ZLibrary.SCREEN_ORIENTATION_REVERSE_LANDSCAPE)); + } + myFBReaderApp.addAction(ActionCode.OPEN_WEB_HELP, new OpenWebHelpAction(this, myFBReaderApp)); + myFBReaderApp.addAction(ActionCode.INSTALL_PLUGINS, new InstallPluginsAction(this, myFBReaderApp)); + + myFBReaderApp.addAction(ActionCode.SWITCH_TO_DAY_PROFILE, new SwitchProfileAction(this, myFBReaderApp, ColorProfile.DAY)); + myFBReaderApp.addAction(ActionCode.SWITCH_TO_NIGHT_PROFILE, new SwitchProfileAction(this, myFBReaderApp, ColorProfile.NIGHT)); + + final Intent intent = getIntent(); + final String action = intent.getAction(); + + myOpenBookIntent = intent; + if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) { + if (FBReaderIntents.Action.CLOSE.equals(action)) { + myCancelIntent = intent; + myOpenBookIntent = null; + } else if (FBReaderIntents.Action.PLUGIN_CRASH.equals(action)) { + myFBReaderApp.ExternalBook = null; + myOpenBookIntent = null; + getCollection().bindToService(this, new Runnable() { + public void run() { + myFBReaderApp.openBook(null, null, null, myNotifier); + } + }); + } + } + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + setStatusBarVisibility(true); + setupMenu(menu); + + return super.onPrepareOptionsMenu(menu); + } + + @Override + public void onOptionsMenuClosed(Menu menu) { + super.onOptionsMenuClosed(menu); + setStatusBarVisibility(false); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + setStatusBarVisibility(false); + return super.onOptionsItemSelected(item); + } + + @Override + protected void onNewIntent(final Intent intent) { + final String action = intent.getAction(); + final Uri data = intent.getData(); + + if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { + super.onNewIntent(intent); + } else if (Intent.ACTION_VIEW.equals(action) + && data != null && "fbreader-action".equals(data.getScheme())) { + myFBReaderApp.runAction(data.getEncodedSchemeSpecificPart(), data.getFragment()); + } else if (Intent.ACTION_VIEW.equals(action) || FBReaderIntents.Action.VIEW.equals(action)) { + myOpenBookIntent = intent; + if (myFBReaderApp.Model == null && myFBReaderApp.ExternalBook != null) { + final BookCollectionShadow collection = getCollection(); + final Book b = FBReaderIntents.getBookExtra(intent, collection); + if (!collection.sameBook(b, myFBReaderApp.ExternalBook)) { + try { + final ExternalFormatPlugin plugin = + (ExternalFormatPlugin) BookUtil.getPlugin( + PluginCollection.Instance(Paths.systemInfo(this)), + myFBReaderApp.ExternalBook + ); + startActivity(PluginUtil.createIntent(plugin, FBReaderIntents.Action.PLUGIN_KILL)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (FBReaderIntents.Action.PLUGIN.equals(action)) { + new RunPluginAction(this, myFBReaderApp, data).run(); + } else if (Intent.ACTION_SEARCH.equals(action)) { + final String pattern = intent.getStringExtra(SearchManager.QUERY); + final Runnable runnable = new Runnable() { + public void run() { + final TextSearchPopup popup = (TextSearchPopup) myFBReaderApp.getPopupById(TextSearchPopup.ID); + popup.initPosition(); + myFBReaderApp.MiscOptions.TextSearchPattern.setValue(pattern); + if (myFBReaderApp.getTextView().search(pattern, true, false, false, false) != 0) { + runOnUiThread(new Runnable() { + public void run() { + myFBReaderApp.showPopup(popup.getId()); + } + }); + } else { + runOnUiThread(new Runnable() { + public void run() { + UIMessageUtil.showErrorMessage(FBReader.this, "textNotFound"); + popup.StartPosition = null; + } + }); + } + } + }; + UIUtil.wait("search", runnable, this); + } else if (FBReaderIntents.Action.CLOSE.equals(intent.getAction())) { + myCancelIntent = intent; + myOpenBookIntent = null; + } else if (FBReaderIntents.Action.PLUGIN_CRASH.equals(intent.getAction())) { + final Book book = FBReaderIntents.getBookExtra(intent, myFBReaderApp.Collection); + myFBReaderApp.ExternalBook = null; + myOpenBookIntent = null; + getCollection().bindToService(this, new Runnable() { + public void run() { + final BookCollectionShadow collection = getCollection(); + Book b = collection.getRecentBook(0); + if (collection.sameBook(b, book)) { + b = collection.getRecentBook(1); + } + myFBReaderApp.openBook(b, null, null, myNotifier); + } + }); + } else { + super.onNewIntent(intent); + } + } + + @Override + protected void onStart() { + super.onStart(); + + getCollection().bindToService(this, new Runnable() { + public void run() { + new Thread() { + public void run() { + getPostponedInitAction().run(); + } + }.start(); + + myFBReaderApp.getViewWidget().repaint(); + } + }); + + initPluginActions(); + + final ZLAndroidLibrary zlibrary = getZLibrary(); + + Config.Instance().runOnConnect(new Runnable() { + public void run() { + final boolean showStatusBar = zlibrary.ShowStatusBarOption.getValue(); + if (showStatusBar != myShowStatusBarFlag) { + finish(); + startActivity(new Intent(FBReader.this, FBReader.class)); + } + zlibrary.ShowStatusBarOption.saveSpecialValue(); + myFBReaderApp.ViewOptions.ColorProfileName.saveSpecialValue(); + SetScreenOrientationAction.setOrientation(FBReader.this, zlibrary.getOrientationOption().getValue()); + } + }); + + ((PopupPanel) myFBReaderApp.getPopupById(TextSearchPopup.ID)).setPanelInfo(this, myRootView); + ((NavigationPopup) myFBReaderApp.getPopupById(NavigationPopup.ID)).setPanelInfo(this, myRootView); + ((PopupPanel) myFBReaderApp.getPopupById(SelectionPopup.ID)).setPanelInfo(this, myRootView); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + switchWakeLock(hasFocus && + getZLibrary().BatteryLevelToTurnScreenOffOption.getValue() < + myFBReaderApp.getBatteryLevel() + ); + } + + private void initPluginActions() { + synchronized (myPluginActions) { + int index = 0; + while (index < myPluginActions.size()) { + myFBReaderApp.removeAction(PLUGIN_ACTION_PREFIX + index++); + } + myPluginActions.clear(); + } + + sendOrderedBroadcast( + new Intent(PluginApi.ACTION_REGISTER), + null, + myPluginInfoReceiver, + null, + RESULT_OK, + null, + null + ); + } + + @Override + public void onPermissionsGranted(int requestCode, List perms) { + Log.i(TAG, "获取权限成功" + perms); + } + + @Override + public void onPermissionsDenied(int requestCode, List perms) { + Log.i(TAG, "获取权限失败" + perms); + } + + private class TipRunner extends Thread { + TipRunner() { + setPriority(MIN_PRIORITY); + } + + public void run() { + final TipsManager manager = new TipsManager(Paths.systemInfo(FBReader.this)); + switch (manager.requiredAction()) { + case Initialize: + startActivity(new Intent( + TipsActivity.INITIALIZE_ACTION, null, FBReader.this, TipsActivity.class + )); + break; + case Show: + startActivity(new Intent( + TipsActivity.SHOW_TIP_ACTION, null, FBReader.this, TipsActivity.class + )); + break; + case Download: + manager.startDownloading(); + break; + case None: + break; + } + } + } + + @Override + protected void onResume() { + super.onResume(); + + myStartTimer = true; + Config.Instance().runOnConnect(new Runnable() { + public void run() { + SyncOperations.enableSync(FBReader.this, myFBReaderApp.SyncOptions); + + final int brightnessLevel = + getZLibrary().ScreenBrightnessLevelOption.getValue(); + if (brightnessLevel != 0) { + getViewWidget().setScreenBrightness(brightnessLevel); + } else { + setScreenBrightnessAuto(); + } + if (getZLibrary().DisableButtonLightsOption.getValue()) { + setButtonLight(false); + } + + getCollection().bindToService(FBReader.this, new Runnable() { + public void run() { + final BookModel model = myFBReaderApp.Model; + if (model == null || model.Book == null) { + return; + } + onPreferencesUpdate(myFBReaderApp.Collection.getBookById(model.Book.getId())); + } + }); + } + }); + + registerReceiver(myBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + IsPaused = false; + myResumeTimestamp = System.currentTimeMillis(); + if (OnResumeAction != null) { + final Runnable action = OnResumeAction; + OnResumeAction = null; + action.run(); + } + + registerReceiver(mySyncUpdateReceiver, new IntentFilter(FBReaderIntents.Event.SYNC_UPDATED)); + + SetScreenOrientationAction.setOrientation(this, getZLibrary().getOrientationOption().getValue()); + if (myCancelIntent != null) { + final Intent intent = myCancelIntent; + myCancelIntent = null; + getCollection().bindToService(this, new Runnable() { + public void run() { + runCancelAction(intent); + } + }); + return; + } else if (myOpenBookIntent != null) { + final Intent intent = myOpenBookIntent; + myOpenBookIntent = null; + getCollection().bindToService(this, new Runnable() { + public void run() { + openBook(intent, null, true); + } + }); + } else if (myFBReaderApp.getCurrentServerBook(null) != null) { + getCollection().bindToService(this, new Runnable() { + public void run() { + myFBReaderApp.useSyncInfo(true, myNotifier); + } + }); + } else if (myFBReaderApp.Model == null && myFBReaderApp.ExternalBook != null) { + getCollection().bindToService(this, new Runnable() { + public void run() { + myFBReaderApp.openBook(myFBReaderApp.ExternalBook, null, null, myNotifier); + } + }); + } else { + getCollection().bindToService(this, new Runnable() { + public void run() { + myFBReaderApp.useSyncInfo(true, myNotifier); + } + }); + } + + PopupPanel.restoreVisibilities(myFBReaderApp); + ApiServerImplementation.sendEvent(this, ApiListener.EVENT_READ_MODE_OPENED); + } + + @Override + protected void onPause() { + SyncOperations.quickSync(this, myFBReaderApp.SyncOptions); + + IsPaused = true; + try { + unregisterReceiver(mySyncUpdateReceiver); + } catch (IllegalArgumentException e) { + } + + try { + unregisterReceiver(myBatteryInfoReceiver); + } catch (IllegalArgumentException e) { + // do nothing, this exception means that myBatteryInfoReceiver was not registered + } + + myFBReaderApp.stopTimer(); + if (getZLibrary().DisableButtonLightsOption.getValue()) { + setButtonLight(true); + } + myFBReaderApp.onWindowClosing(); + + super.onPause(); + } + + @Override + protected void onStop() { + ApiServerImplementation.sendEvent(this, ApiListener.EVENT_READ_MODE_CLOSED); + PopupPanel.removeAllWindows(myFBReaderApp, this); + super.onStop(); + } + + @Override + protected void onDestroy() { + getCollection().unbind(); + unbindService(DataConnection); + super.onDestroy(); + } + + @Override + public void onLowMemory() { + myFBReaderApp.onWindowClosing(); + super.onLowMemory(); + } + + @Override + public boolean onSearchRequested() { + final FBReaderApp.PopupPanel popup = myFBReaderApp.getActivePopup(); + myFBReaderApp.hideActivePopup(); + if (DeviceType.Instance().hasStandardSearchDialog()) { + final SearchManager manager = (SearchManager) getSystemService(SEARCH_SERVICE); + manager.setOnCancelListener(new SearchManager.OnCancelListener() { + public void onCancel() { + if (popup != null) { + myFBReaderApp.showPopup(popup.getId()); + } + manager.setOnCancelListener(null); + } + }); + startSearch(myFBReaderApp.MiscOptions.TextSearchPattern.getValue(), true, null, false); + } else { + SearchDialogUtil.showDialog( + this, FBReader.class, myFBReaderApp.MiscOptions.TextSearchPattern.getValue(), new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface di) { + if (popup != null) { + myFBReaderApp.showPopup(popup.getId()); + } + } + } + ); + } + return true; + } + + public void showSelectionPanel() { + final ZLTextView view = myFBReaderApp.getTextView(); + ((SelectionPopup) myFBReaderApp.getPopupById(SelectionPopup.ID)) + .move(view.getSelectionStartY(), view.getSelectionEndY()); + myFBReaderApp.showPopup(SelectionPopup.ID); + } + + public void hideSelectionPanel() { + final FBReaderApp.PopupPanel popup = myFBReaderApp.getActivePopup(); + if (popup != null && popup.getId() == SelectionPopup.ID) { + myFBReaderApp.hideActivePopup(); + } + } + + private void onPreferencesUpdate(Book book) { + AndroidFontUtil.clearFontCache(); + myFBReaderApp.onBookUpdated(book); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + default: + super.onActivityResult(requestCode, resultCode, data); + break; + case REQUEST_PREFERENCES: + if (resultCode != RESULT_DO_NOTHING && data != null) { + final Book book = FBReaderIntents.getBookExtra(data, myFBReaderApp.Collection); + if (book != null) { + getCollection().bindToService(this, new Runnable() { + public void run() { + onPreferencesUpdate(book); + } + }); + } + } + break; + case REQUEST_CANCEL_MENU: + runCancelAction(data); + break; + } + } + + private void runCancelAction(Intent intent) { + final CancelMenuHelper.ActionType type; + try { + type = CancelMenuHelper.ActionType.valueOf( + intent.getStringExtra(FBReaderIntents.Key.TYPE) + ); + } catch (Exception e) { + // invalid (or null) type value + return; + } + Bookmark bookmark = null; + if (type == CancelMenuHelper.ActionType.returnTo) { + bookmark = FBReaderIntents.getBookmarkExtra(intent); + if (bookmark == null) { + return; + } + } + myFBReaderApp.runCancelAction(type, bookmark); + } + + public void navigate() { + ((NavigationPopup) myFBReaderApp.getPopupById(NavigationPopup.ID)).runNavigation(); + } + + private Menu addSubmenu(Menu menu, String id) { + return menu.addSubMenu(ZLResource.resource("menu").getResource(id).getValue()); + } + + private void addMenuItem(Menu menu, String actionId, Integer iconId, String name) { + if (name == null) { + name = ZLResource.resource("menu").getResource(actionId).getValue(); + } + final MenuItem menuItem = menu.add(name); + if (iconId != null) { + menuItem.setIcon(iconId); + } + menuItem.setOnMenuItemClickListener(myMenuListener); + myMenuItemMap.put(menuItem, actionId); + } + + private void addMenuItem(Menu menu, String actionId, String name) { + addMenuItem(menu, actionId, null, name); + } + + private void addMenuItem(Menu menu, String actionId, int iconId) { + addMenuItem(menu, actionId, iconId, null); + } + + private void addMenuItem(Menu menu, String actionId) { + addMenuItem(menu, actionId, null, null); + } + + private void fillMenu(Menu menu, List nodes) { + for (MenuNode n : nodes) { + if (n instanceof MenuNode.Item) { + final Integer iconId = ((MenuNode.Item) n).IconId; + if (iconId != null) { + addMenuItem(menu, n.Code, iconId); + } else { + addMenuItem(menu, n.Code); + } + } else /* if (n instanceof MenuNode.Submenu) */ { + final Menu submenu = addSubmenu(menu, n.Code); + fillMenu(submenu, ((MenuNode.Submenu) n).Children); + } + } + } + + private void setupMenu(Menu menu) { + final String menuLanguage = ZLResource.getLanguageOption().getValue(); + if (menuLanguage.equals(myMenuLanguage)) { + return; + } + myMenuLanguage = menuLanguage; + + menu.clear(); + fillMenu(menu, MenuData.topLevelNodes()); + synchronized (myPluginActions) { + int index = 0; + for (PluginApi.ActionInfo info : myPluginActions) { + if (info instanceof PluginApi.MenuActionInfo) { + addMenuItem( + menu, + PLUGIN_ACTION_PREFIX + index++, + ((PluginApi.MenuActionInfo) info).MenuItemName + ); + } + } + } + + refresh(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + setupMenu(menu); + + return true; + } + + protected void onPluginNotFound(final Book book) { + final BookCollectionShadow collection = getCollection(); + collection.bindToService(this, new Runnable() { + public void run() { + final Book recent = collection.getRecentBook(0); + if (recent != null && !collection.sameBook(recent, book)) { + myFBReaderApp.openBook(recent, null, null, null); + } else { + myFBReaderApp.openHelpBook(); + } + } + }); + } + + private void setStatusBarVisibility(boolean visible) { + final ZLAndroidLibrary zlibrary = getZLibrary(); + if (DeviceType.Instance() != DeviceType.KINDLE_FIRE_1ST_GENERATION && !myShowStatusBarFlag) { + if (visible) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + } else { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + } + } + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return (myMainView != null && myMainView.onKeyDown(keyCode, event)) || super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + return (myMainView != null && myMainView.onKeyUp(keyCode, event)) || super.onKeyUp(keyCode, event); + } + + private void setButtonLight(boolean enabled) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { + setButtonLightInternal(enabled); + } + } + + @TargetApi(Build.VERSION_CODES.FROYO) + private void setButtonLightInternal(boolean enabled) { + final WindowManager.LayoutParams attrs = getWindow().getAttributes(); + attrs.buttonBrightness = enabled ? -1.0f : 0.0f; + getWindow().setAttributes(attrs); + } + + private PowerManager.WakeLock myWakeLock; + private boolean myWakeLockToCreate; + private boolean myStartTimer; + + public final void createWakeLock() { + if (myWakeLockToCreate) { + synchronized (this) { + if (myWakeLockToCreate) { + myWakeLockToCreate = false; + myWakeLock = + ((PowerManager) getSystemService(POWER_SERVICE)) + .newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "FBReader"); + myWakeLock.acquire(); + } + } + } + if (myStartTimer) { + myFBReaderApp.startTimer(); + myStartTimer = false; + } + } + + private final void switchWakeLock(boolean on) { + if (on) { + if (myWakeLock == null) { + myWakeLockToCreate = true; + } + } else { + if (myWakeLock != null) { + synchronized (this) { + if (myWakeLock != null) { + myWakeLock.release(); + myWakeLock = null; + } + } + } + } + } + + private BroadcastReceiver myBatteryInfoReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + final int level = intent.getIntExtra("level", 100); + final ZLAndroidApplication application = (ZLAndroidApplication) getApplication(); + setBatteryLevel(level); + switchWakeLock( + hasWindowFocus() && + getZLibrary().BatteryLevelToTurnScreenOffOption.getValue() < level + ); + } + }; + + private BookCollectionShadow getCollection() { + return (BookCollectionShadow) myFBReaderApp.Collection; + } + + // methods from ZLApplicationWindow interface + @Override + public void showErrorMessage(String key) { + UIMessageUtil.showErrorMessage(this, key); + } + + @Override + public void showErrorMessage(String key, String parameter) { + UIMessageUtil.showErrorMessage(this, key, parameter); + } + + @Override + public FBReaderApp.SynchronousExecutor createExecutor(String key) { + return UIUtil.createExecutor(this, key); + } + + private int myBatteryLevel; + + @Override + public int getBatteryLevel() { + return myBatteryLevel; + } + + private void setBatteryLevel(int percent) { + myBatteryLevel = percent; + } + + @Override + public void close() { + finish(); + } + + @Override + public ZLViewWidget getViewWidget() { + return myMainView; + } + + private final HashMap myMenuItemMap = new HashMap(); + + private final MenuItem.OnMenuItemClickListener myMenuListener = + new MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + myFBReaderApp.runAction(myMenuItemMap.get(item)); + return true; + } + }; + + @Override + public void refresh() { + runOnUiThread(new Runnable() { + public void run() { + for (Map.Entry entry : myMenuItemMap.entrySet()) { + final String actionId = entry.getValue(); + final MenuItem menuItem = entry.getKey(); + menuItem.setVisible(myFBReaderApp.isActionVisible(actionId) && myFBReaderApp.isActionEnabled(actionId)); + switch (myFBReaderApp.isActionChecked(actionId)) { + case TRUE: + menuItem.setCheckable(true); + menuItem.setChecked(true); + break; + case FALSE: + menuItem.setCheckable(true); + menuItem.setChecked(false); + break; + case UNDEFINED: + menuItem.setCheckable(false); + break; + } + } + } + }); + } + + @Override + public void processException(Exception exception) { + exception.printStackTrace(); + + final Intent intent = new Intent( + FBReaderIntents.Action.ERROR, + new Uri.Builder().scheme(exception.getClass().getSimpleName()).build() + ); + intent.setPackage(FBReaderIntents.DEFAULT_PACKAGE); + intent.putExtra(ErrorKeys.MESSAGE, exception.getMessage()); + final StringWriter stackTrace = new StringWriter(); + exception.printStackTrace(new PrintWriter(stackTrace)); + intent.putExtra(ErrorKeys.STACKTRACE, stackTrace.toString()); /* if (exception instanceof BookReadingException) { final ZLFile file = ((BookReadingException)exception).File; @@ -1011,43 +1062,50 @@ public void processException(Exception exception) { } } */ - try { - startActivity(intent); - } catch (ActivityNotFoundException e) { - // ignore - e.printStackTrace(); - } - } - - @Override - public void setWindowTitle(final String title) { - runOnUiThread(new Runnable() { - public void run() { - setTitle(title); - } - }); - } - - private BroadcastReceiver mySyncUpdateReceiver = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - myFBReaderApp.useSyncInfo(myResumeTimestamp + 10 * 1000 > System.currentTimeMillis(), myNotifier); - } - }; - - public void outlineRegion(ZLTextRegion.Soul soul) { - myFBReaderApp.getTextView().outlineRegion(soul); - myFBReaderApp.getViewWidget().repaint(); - } - - public void hideOutline() { - myFBReaderApp.getTextView().hideOutline(); - myFBReaderApp.getViewWidget().repaint(); - } - - public void hideDictionarySelection() { - myFBReaderApp.getTextView().hideOutline(); - myFBReaderApp.getTextView().removeHighlightings(DictionaryHighlighting.class); - myFBReaderApp.getViewWidget().reset(); - myFBReaderApp.getViewWidget().repaint(); - } + try { + startActivity(intent); + } catch (ActivityNotFoundException e) { + // ignore + e.printStackTrace(); + } + } + + @Override + public void setWindowTitle(final String title) { + runOnUiThread(new Runnable() { + public void run() { + setTitle(title); + } + }); + } + + private BroadcastReceiver mySyncUpdateReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + myFBReaderApp.useSyncInfo(myResumeTimestamp + 10 * 1000 > System.currentTimeMillis(), myNotifier); + } + }; + + public void outlineRegion(ZLTextRegion.Soul soul) { + myFBReaderApp.getTextView().outlineRegion(soul); + myFBReaderApp.getViewWidget().repaint(); + } + + public void hideOutline() { + myFBReaderApp.getTextView().hideOutline(); + myFBReaderApp.getViewWidget().repaint(); + } + + public void hideDictionarySelection() { + myFBReaderApp.getTextView().hideOutline(); + myFBReaderApp.getTextView().removeHighlightings(DictionaryHighlighting.class); + myFBReaderApp.getViewWidget().reset(); + myFBReaderApp.getViewWidget().repaint(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); + } } diff --git a/fBReader/src/main/java/org/geometerplus/android/fbreader/ShowMenuAction.java b/fBReader/src/main/java/org/geometerplus/android/fbreader/ShowMenuAction.java index 50e032b..fc0333c 100644 --- a/fBReader/src/main/java/org/geometerplus/android/fbreader/ShowMenuAction.java +++ b/fBReader/src/main/java/org/geometerplus/android/fbreader/ShowMenuAction.java @@ -25,7 +25,6 @@ class ShowMenuAction extends FBAndroidAction { ShowMenuAction(FBReader baseActivity, FBReaderApp fbreader) { super(baseActivity, fbreader); } - @Override protected void run(Object ... params) { BaseActivity.openOptionsMenu(); diff --git a/fBReader/src/main/java/org/geometerplus/android/fbreader/TranslateAction.java b/fBReader/src/main/java/org/geometerplus/android/fbreader/TranslateAction.java new file mode 100644 index 0000000..dc19c64 --- /dev/null +++ b/fBReader/src/main/java/org/geometerplus/android/fbreader/TranslateAction.java @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2007-2015 FBReader.ORG Limited + * + * 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.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.SoundPool; +import android.os.Build; +import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.baidu.translate.demo.MD5; + +import org.geometerplus.fbreader.fbreader.FBReaderApp; +import org.geometerplus.zlibrary.ui.android.R; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import dyg.activity.TransWebViewActivity; +import dyg.beans.CiBaWordBeanJson; +import dyg.beans.GsonBuildList; +import dyg.net.LoveFamousBookNet; +import dyg.net.LoveFamousMp3FileDownload; +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +class TranslateAction extends FBAndroidAction { + private final static String TAG = "TranslateAction"; + private Activity activity; + private Dialog dialog; + private TextView trans_more, trans_none, phonetic_content_en, phonetic_content_us; + private ImageView read_en, read_us; + private LinearLayout symbolLayout, trans_phonetic; + private int screen_height; + private Pattern pattern = Pattern.compile("[a-z]+"); + private SoundPool mSoundPool; + + + TranslateAction(FBReader baseActivity, FBReaderApp fbreader) { + super(baseActivity, fbreader); + this.activity = baseActivity; + DisplayMetrics metrics = activity.getResources().getDisplayMetrics(); + screen_height = metrics.heightPixels; + dialog = new Dialog(activity); + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); + View view = LayoutInflater.from(activity).inflate(R.layout.dialog_translate, null); + dialog.setContentView(view); + trans_phonetic = (LinearLayout) view.findViewById(R.id.trans_phonetic_layout); + symbolLayout = (LinearLayout) view.findViewById(R.id.symbol_layout); + trans_none = (TextView) view.findViewById(R.id.trans_none); + trans_more = (TextView) view.findViewById(R.id.trans_more); + + phonetic_content_en = (TextView) view.findViewById(R.id.phonetic_content_en); + phonetic_content_en.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + prounce(read_en.getTag()); + } + }); + + phonetic_content_us = (TextView) view.findViewById(R.id.phonetic_content_us); + phonetic_content_us.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + prounce(read_us.getTag()); + } + }); + read_en = (ImageView) view.findViewById(R.id.read_en); + read_us = (ImageView) view.findViewById(R.id.read_us); + read_en.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + prounce(read_en.getTag()); + } + }); + read_us.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + prounce(read_us.getTag()); + } + }); + trans_more.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (dialog.isShowing()) { + dialog.dismiss(); + } + String string = (String) trans_more.getTag(); + if (string == null) { + return; + } + Intent intent = new Intent(activity, TransWebViewActivity.class); + intent.putExtra("key", string); + activity.startActivity(intent); +// Toast.makeText(activity, "显示查看更多", Toast.LENGTH_LONG).show(); + } + }); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog1) { + symbolLayout.setVisibility(View.GONE); + trans_phonetic.setVisibility(View.GONE); + trans_none.setVisibility(View.GONE); + trans_more.setVisibility(View.GONE); + trans_more.setTag(null); + symbolLayout.removeAllViews(); + } + }); + init(); + } + + private void prounce(final Object tag) { + if (tag == null) { + Toast.makeText(activity, "sorry! no word to read", Toast.LENGTH_LONG).show(); + return; + } + if (tag.toString().equals("")) { + Log.e(TAG, "sorry! no word to read"); + Toast.makeText(activity, "sorry! no word to read", Toast.LENGTH_LONG).show(); + return; + } + + Retrofit retrofit = new Retrofit.Builder().baseUrl("http://yy.cc").build(); + LoveFamousMp3FileDownload fileDownload = retrofit.create(LoveFamousMp3FileDownload.class); + Call call = fileDownload.downloadMp3((String) tag); + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful()) { + ResponseBody body = response.body(); + File file = writeResponseBodyToDisk(body, (String) tag); + if (file != null) { + final int id = mSoundPool.load(file.getAbsolutePath(), 1); + mSoundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener + () { + @Override + public void onLoadComplete(SoundPool soundPool, int sampleId, int + status) { + mSoundPool.play(id, 1.0f, 1.0f, 1, 0, 1.0f); + } + }); + + } + + } + + } + + @Override + public void onFailure(Call call, Throwable t) { + System.out.println("请求失败"); + System.out.println(t.getMessage()); + Toast.makeText(activity, "" + t.getMessage(), Toast.LENGTH_SHORT).show(); +// new AlertDialog.Builder(activity).setTitle("error").setMessage(t.getMessage()) +// .create().show(); + } + }); +// retrofit. + + } + + @Override + protected void run(final Object... params) { + + if (params[0] == null) { + Log.i(TAG, "run: TranslateAction param is empty return"); + return; + } + + String key = (String) params[0]; + if (TextUtils.isEmpty(key)) { + Log.i(TAG, "run: TranslateAction translate key is empty return"); + return; + } + key = key.trim().toLowerCase(); + if (TextUtils.isEmpty(key)) { + Log.i(TAG, "run: TranslateAction translate key is empty return"); + return; + } + Matcher matcher = pattern.matcher(key); + if (matcher.find()) { + key = matcher.toMatchResult().group(); + } else { + Log.i(TAG, "run: TranslateAction translate key is empty return"); + return; + } + + Retrofit retrofit = new Retrofit.Builder() + .baseUrl("http://dict-co.iciba.com/") + .addConverterFactory(GsonConverterFactory.create(GsonBuildList.buildGson())) + .build(); + LoveFamousBookNet lbn = retrofit.create(LoveFamousBookNet.class); + HashMap map = new HashMap(); + map.put("w", key); + map.put("type", "json"); + map.put("key", "297EB35CDF5FEEEFD6A13200E46FA720"); + Call res = lbn.getWords(map); + res.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response + response) { + CiBaWordBeanJson ciBaWordBeanJson = response.body(); + if (ciBaWordBeanJson != null) { + List symbols = ciBaWordBeanJson.getSymbols(); + CiBaWordBeanJson.SymbolsBean symbolsBean = symbols.get(0); + if (symbolsBean == null) { + trans_none.setVisibility(View.VISIBLE); + } else { + String word_name = ciBaWordBeanJson.getWord_name(); + trans_none.setVisibility(View.GONE); + trans_more.setTag(word_name); + // add phonetic + String enPhonetic = symbolsBean.getPh_en(); + String usPhonetic = symbolsBean.getPh_am(); + String enPhonetic_mp3 = symbolsBean.getPh_en_mp3(); + String usPhonetic_mp3 = symbolsBean.getPh_am_mp3(); + String tts_mp3 = symbolsBean.getPh_tts_mp3(); + if (TextUtils.isEmpty(enPhonetic_mp3)) { + if (!TextUtils.isEmpty(tts_mp3)) { + enPhonetic_mp3 = tts_mp3; + } else if (!TextUtils.isEmpty(usPhonetic_mp3)) { + enPhonetic = usPhonetic_mp3; + } + } + if (TextUtils.isEmpty(usPhonetic_mp3)) { + if (!TextUtils.isEmpty(tts_mp3)) { + usPhonetic_mp3 = tts_mp3; + } else if (!TextUtils.isEmpty(enPhonetic_mp3)) { + usPhonetic_mp3 = enPhonetic_mp3; + } + } + phonetic_content_en.setText("[" + enPhonetic + "]"); + phonetic_content_us.setText("[" + usPhonetic + "]"); + read_en.setTag(enPhonetic_mp3); + read_us.setTag(usPhonetic_mp3); + // add symbol + TextView textView = (TextView) LayoutInflater.from(activity).inflate(R + .layout.dialog_symbol, null); + StringBuilder builder = new StringBuilder(); + List parts = symbolsBean.getParts(); + for (int i = 0; i < parts.size(); i++) { + CiBaWordBeanJson.SymbolsBean.PartsBean partsBean = parts.get(i); + builder.append(partsBean.getPart()).append(" ").append + (getTransChinese(builder, partsBean.getMeans())).append('\n'); + } + textView.setText(builder.toString()); + symbolLayout.addView(textView); + trans_phonetic.setVisibility(View.VISIBLE); + symbolLayout.setVisibility(View.VISIBLE); + trans_more.setVisibility(View.VISIBLE); + } + int x = (int) params[1]; + int y = (int) params[2]; + if ((x + y) > 0) { + Window window = dialog.getWindow(); + window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager + .LayoutParams.WRAP_CONTENT); + WindowManager.LayoutParams wlp = window.getAttributes(); + wlp.gravity = Gravity.TOP | Gravity.START; + int dialogH = window.getDecorView().getHeight(); + int last_distance = screen_height - y; + if (dialogH < last_distance) { + wlp.y = y; + // todo arrow 朝上 + } else { + wlp.y = y - 20; + // todo arrow 在下面朝下 + } + wlp.x = 0; + window.setAttributes(wlp); + } + dialog.show(); + } + + } + + @Override + public void onFailure(Call call, Throwable t) { + System.out.println("请求失败"); + System.out.println(t.getMessage()); + new AlertDialog.Builder(activity).setTitle("error").setMessage(t.getMessage()) + .create().show(); + } + }); + + + } + + private String getTransChinese(StringBuilder builder, List means) { + for (int i = 0; i < means.size(); i++) { + String mean = means.get(i); + builder.append(mean); + if (i != means.size() - 1) { + builder.append(";"); + } + } + return " "; + } + + private void init() { + if (Build.VERSION.SDK_INT >= 21) { + //SDK_INT >= 21时,才能使用SoundPool.Builder创建SoundPool + SoundPool.Builder builder = new SoundPool.Builder(); + + //可同时播放的音频流 + builder.setMaxStreams(5); + + //音频属性的Builder + AudioAttributes.Builder attrBuild = new AudioAttributes.Builder(); + + //音频类型 + attrBuild.setLegacyStreamType(AudioManager.STREAM_MUSIC); + + builder.setAudioAttributes(attrBuild.build()); + + mSoundPool = builder.build(); + } else { + //低版本的构造方法,已经deprecated了 + mSoundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0); + } + } + + private File writeResponseBodyToDisk(ResponseBody body, String url) { + try { + String filename = MD5.md5(url); + // todo change the file location/name according to your needs + File futureStudioIconFile = new File(activity.getCacheDir() + File.separator + + filename); + + InputStream inputStream = null; + OutputStream outputStream = null; + + try { + byte[] fileReader = new byte[4096]; + + long fileSize = body.contentLength(); + long fileSizeDownloaded = 0; + + inputStream = body.byteStream(); + outputStream = new FileOutputStream(futureStudioIconFile); + + while (true) { + int read = inputStream.read(fileReader); + + if (read == -1) { + break; + } + + outputStream.write(fileReader, 0, read); + + fileSizeDownloaded += read; + + Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize); + } + + outputStream.flush(); + + return futureStudioIconFile; + } catch (IOException e) { + return null; + } finally { + if (inputStream != null) { + inputStream.close(); + } + + if (outputStream != null) { + outputStream.close(); + } + } + } catch (IOException e) { + return null; + } + } + +} diff --git a/fBReader/src/main/java/org/geometerplus/android/fbreader/image/ImageViewActivity.java b/fBReader/src/main/java/org/geometerplus/android/fbreader/image/ImageViewActivity.java index af77317..87542d4 100644 --- a/fBReader/src/main/java/org/geometerplus/android/fbreader/image/ImageViewActivity.java +++ b/fBReader/src/main/java/org/geometerplus/android/fbreader/image/ImageViewActivity.java @@ -21,20 +21,25 @@ import android.app.Activity; import android.content.Intent; -import android.graphics.*; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; import android.os.Bundle; -import android.util.FloatMath; -import android.view.*; +import android.view.MotionEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; -import org.geometerplus.zlibrary.core.image.*; +import org.geometerplus.android.util.OrientationUtil; +import org.geometerplus.zlibrary.core.image.ZLFileImage; +import org.geometerplus.zlibrary.core.image.ZLImageData; +import org.geometerplus.zlibrary.core.image.ZLImageManager; 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.library.ZLAndroidLibrary; import org.geometerplus.zlibrary.ui.android.util.ZLAndroidColorUtil; -import org.geometerplus.android.util.OrientationUtil; - public class ImageViewActivity extends Activity { public static final String URL_KEY = "fbreader.imageview.url"; public static final String BACKGROUND_COLOR_KEY = "fbreader.imageview.background"; @@ -258,7 +263,7 @@ private boolean onDoubleTouchEvent(MotionEvent event) { myStartPinchDistance2 = distance2; myStartZoomFactor = myZoomFactor; } else { - myZoomFactor = myStartZoomFactor * FloatMath.sqrt(distance2 / myStartPinchDistance2); + myZoomFactor = (float) (myStartZoomFactor * Math.sqrt(distance2 / myStartPinchDistance2)); postInvalidate(); } } diff --git a/fBReader/src/main/java/org/geometerplus/android/fbreader/network/BookDownloaderService.java b/fBReader/src/main/java/org/geometerplus/android/fbreader/network/BookDownloaderService.java index 275d41b..e399436 100644 --- a/fBReader/src/main/java/org/geometerplus/android/fbreader/network/BookDownloaderService.java +++ b/fBReader/src/main/java/org/geometerplus/android/fbreader/network/BookDownloaderService.java @@ -19,361 +19,376 @@ package org.geometerplus.android.fbreader.network; -import java.util.*; -import java.io.*; - -import android.app.*; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; import android.content.Intent; import android.net.Uri; -import android.os.*; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; import android.widget.RemoteViews; import android.widget.Toast; -import org.geometerplus.zlibrary.core.network.*; +import org.geometerplus.android.fbreader.FBReader; +import org.geometerplus.android.fbreader.NotificationUtil; +import org.geometerplus.android.fbreader.libraryService.BookCollectionShadow; +import org.geometerplus.android.fbreader.network.auth.ServiceNetworkContext; +import org.geometerplus.fbreader.network.urlInfo.BookUrlInfo; +import org.geometerplus.fbreader.network.urlInfo.UrlInfo; +import org.geometerplus.zlibrary.core.network.ZLNetworkContext; +import org.geometerplus.zlibrary.core.network.ZLNetworkException; +import org.geometerplus.zlibrary.core.network.ZLNetworkRequest; import org.geometerplus.zlibrary.core.resources.ZLResource; import org.geometerplus.zlibrary.core.util.MimeType; import org.geometerplus.zlibrary.ui.android.R; import org.geometerplus.zlibrary.ui.android.network.SQLiteCookieDatabase; -import org.geometerplus.fbreader.network.urlInfo.UrlInfo; -import org.geometerplus.fbreader.network.urlInfo.BookUrlInfo; - -import org.geometerplus.android.fbreader.FBReader; -import org.geometerplus.android.fbreader.NotificationUtil; -import org.geometerplus.android.fbreader.libraryService.BookCollectionShadow; -import org.geometerplus.android.fbreader.network.auth.ServiceNetworkContext; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; public class BookDownloaderService extends Service { - private final ZLNetworkContext myNetworkContext = new ServiceNetworkContext(this); - - public interface Key { - String FROM_SYNC = "fbreader.downloader.from.sync"; - String BOOK_TITLE = "fbreader.downloader.book.title"; - String BOOK_KIND = "fbreader.downloader.book.kind"; - String BOOK_MIME = "fbreader.downloader.book.mime"; - String CLEAN_URL = "fbreader.downloader.clean.url"; - String SHOW_NOTIFICATIONS = "fbreader.downloader.show.notifications"; - String NOTIFICATION_TO_DISMISS_ID = "fbreader.downloader.notification.id"; - } - - public interface Notifications { - int DOWNLOAD_STARTED = 0x0001; - int ALREADY_IN_PROGRESS = 0x0002; - - int ALL = 0x0003; - } - - private Set myDownloadingURLs = Collections.synchronizedSet(new HashSet()); - private Set myOngoingNotifications = new HashSet(); - - private volatile int myServiceCounter; - - private void doStart() { - ++myServiceCounter; - } - - private void doStop() { - if (--myServiceCounter == 0) { - stopSelf(); - } - } - - public static ZLResource getResource() { - return ZLResource.resource("bookDownloader"); - } - - @Override - public IBinder onBind(Intent intent) { - return new BookDownloaderInterface.Stub() { - public boolean isBeingDownloaded(String url) { - return myDownloadingURLs.contains(url); - } - }; - } - - @Override - public void onDestroy() { - for (int notificationId : myOngoingNotifications) { - NotificationUtil.drop(this, notificationId); - } - myOngoingNotifications.clear(); - super.onDestroy(); - } - - @Override - public void onStart(Intent intent, int startId) { - super.onStart(intent, startId); - doStart(); - - final Uri uri = intent != null ? intent.getData() : null; - if (uri == null) { - doStop(); - return; - } - intent.setData(null); - - if (intent.getBooleanExtra(Key.FROM_SYNC, false)) { - final int notificationId = intent.getIntExtra( - Key.NOTIFICATION_TO_DISMISS_ID, - NotificationUtil.MISSING_BOOK_ID - ); - NotificationUtil.drop(this, notificationId); - } - - final int notifications = intent.getIntExtra(Key.SHOW_NOTIFICATIONS, 0); - - final String url = uri.toString(); - final MimeType mime = MimeType.get(intent.getStringExtra(Key.BOOK_MIME)); - UrlInfo.Type referenceType = (UrlInfo.Type)intent.getSerializableExtra(Key.BOOK_KIND); - if (referenceType == null) { - referenceType = UrlInfo.Type.Book; - } - - String cleanURL = intent.getStringExtra(Key.CLEAN_URL); - if (cleanURL == null) { - cleanURL = url; - } - - if (myDownloadingURLs.contains(url)) { - if ((notifications & Notifications.ALREADY_IN_PROGRESS) != 0) { - showMessage("alreadyDownloading"); - } - doStop(); - return; - } - - final String fileName = BookUrlInfo.makeBookFileName(cleanURL, mime, referenceType); - if (fileName == null) { - doStop(); - return; - } - - int index = fileName.lastIndexOf(File.separator); - if (index != -1) { - final String dir = fileName.substring(0, index); - final File dirFile = new File(dir); - if (!dirFile.exists() && !dirFile.mkdirs()) { - showMessage("cannotCreateDirectory", dirFile.getPath()); - doStop(); - return; - } - if (!dirFile.exists() || !dirFile.isDirectory()) { - showMessage("cannotCreateDirectory", dirFile.getPath()); - doStop(); - return; - } - } - - final File fileFile = new File(fileName); - if (fileFile.exists()) { - if (!fileFile.isFile()) { - showMessage("cannotCreateFile", fileFile.getPath()); - doStop(); - return; - } - // TODO: question box: redownload? - doStop(); - startActivity(getFBReaderIntent(fileFile)); - return; - } - String title = intent.getStringExtra(Key.BOOK_TITLE); - if (title == null || title.length() == 0) { - title = fileFile.getName(); - } - if ((notifications & Notifications.DOWNLOAD_STARTED) != 0) { - showMessage("downloadStarted"); - } - startFileDownload(url, fileFile, title); - } - - private void showMessageText(String text) { - Toast.makeText( - getApplicationContext(), - text, - Toast.LENGTH_LONG - ).show(); - } - - private void showMessage(String key) { - showMessageText(getResource().getResource(key).getValue()); - } - - private void showMessage(String key, String parameter) { - showMessageText(getResource().getResource(key).getValue().replace("%s", parameter)); - } - - private Intent getFBReaderIntent(final File file) { - final Intent intent = new Intent(getApplicationContext(), FBReader.class); - if (file != null) { - intent.setAction(Intent.ACTION_VIEW).setData(Uri.fromFile(file)); - } - return intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - } - - private Notification createDownloadFinishNotification(File file, String title, boolean success) { - final ZLResource resource = getResource(); - final String tickerText = success ? - resource.getResource("downloadCompleteTicker").getValue() : - resource.getResource("downloadFailedTicker").getValue(); - final String contentText = success ? - resource.getResource("downloadComplete").getValue() : - resource.getResource("downloadFailed").getValue(); - final Notification notification = new Notification( - android.R.drawable.stat_sys_download_done, - tickerText, - System.currentTimeMillis() - ); - notification.flags |= Notification.FLAG_AUTO_CANCEL; - final Intent intent = success ? getFBReaderIntent(file) : new Intent(); - final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0); - notification.setLatestEventInfo(getApplicationContext(), title, contentText, contentIntent); - return notification; - } - - private Notification createDownloadProgressNotification(String title) { - final RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.download_notification); - contentView.setTextViewText(R.id.download_notification_title, title); - contentView.setTextViewText(R.id.download_notification_progress_text, ""); - contentView.setProgressBar(R.id.download_notification_progress_bar, 100, 0, true); - - final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(), 0); - - final Notification notification = new Notification(); - notification.icon = android.R.drawable.stat_sys_download; - notification.flags |= Notification.FLAG_ONGOING_EVENT; - notification.contentView = contentView; - notification.contentIntent = contentIntent; - - return notification; - } - - private void sendDownloaderCallback() { - sendBroadcast( - new Intent(getApplicationContext(), ListenerCallback.class) - ); - } - - private void startFileDownload(final String urlString, final File file, final String title) { - myDownloadingURLs.add(urlString); - sendDownloaderCallback(); - - final int notificationId = NotificationUtil.getDownloadId(file.getPath()); - final Notification progressNotification = createDownloadProgressNotification(title); - - final NotificationManager notificationManager = - (NotificationManager)getSystemService(NOTIFICATION_SERVICE); - myOngoingNotifications.add(Integer.valueOf(notificationId)); - notificationManager.notify(notificationId, progressNotification); - - final int MESSAGE_PROGRESS = 0; - final int MESSAGE_FINISH = 1; - - final Handler handler = new Handler() { - public void handleMessage(Message message) { - switch (message.what) { - case MESSAGE_PROGRESS: - { - final int progress = message.arg1; - final RemoteViews contentView = (RemoteViews)progressNotification.contentView; - final boolean showPercent = progress >= 0; - contentView.setTextViewText( - R.id.download_notification_progress_text, - showPercent ? progress + "%" : "" - ); - contentView.setProgressBar( - R.id.download_notification_progress_bar, - 100, showPercent ? progress : 0, !showPercent - ); - notificationManager.notify(notificationId, progressNotification); - break; - } - case MESSAGE_FINISH: - myDownloadingURLs.remove(urlString); - NotificationUtil.drop(BookDownloaderService.this, notificationId); - myOngoingNotifications.remove(Integer.valueOf(notificationId)); - notificationManager.notify( - notificationId, - createDownloadFinishNotification(file, title, message.arg1 != 0) - ); - sendDownloaderCallback(); - doStop(); - break; - } - } - }; - - final ZLNetworkRequest request = new ZLNetworkRequest.Get(urlString) { - public void handleStream(InputStream inputStream, int length) throws IOException, ZLNetworkException { - final int updateIntervalMillis = 1000; // FIXME: remove hardcoded time constant - - int downloadedPart = 0; - long progressTime = System.currentTimeMillis() + updateIntervalMillis; - if (length <= 0) { - handler.sendMessage(handler.obtainMessage(MESSAGE_PROGRESS, -1, 0, null)); - } - final OutputStream outStream; - try { - outStream = new FileOutputStream(file); - } catch (FileNotFoundException ex) { - throw ZLNetworkException.forCode(ZLNetworkException.ERROR_CREATE_FILE, file.getPath()); - } - try { - final byte[] buffer = new byte[8192]; - while (true) { - final int size = inputStream.read(buffer); - if (size <= 0) { - break; - } - downloadedPart += size; - if (length > 0) { - final long currentTime = System.currentTimeMillis(); - if (currentTime > progressTime) { - progressTime = currentTime + updateIntervalMillis; - handler.sendMessage(handler.obtainMessage( - MESSAGE_PROGRESS, downloadedPart * 100 / length, 0, null - )); - } - } - outStream.write(buffer, 0, size); - } - final BookCollectionShadow collection = new BookCollectionShadow(); - collection.bindToService(BookDownloaderService.this, new Runnable() { - @Override - public void run() { - collection.rescan(file.getPath()); - collection.unbind(); - } - }); - } finally { - outStream.close(); - } - } - }; - - final Thread downloader = new Thread(new Runnable() { - public void run() { - boolean success = false; - try { - SQLiteCookieDatabase.init(BookDownloaderService.this); - myNetworkContext.perform(request); - success = true; - } catch (final ZLNetworkException e) { - e.printStackTrace(); - final String title = getResource().getResource("downloadFailed").getValue(); - handler.post(new Runnable() { - public void run() { - showMessageText(title + ": " + e.getMessage()); - } - }); - file.delete(); - } finally { - handler.sendMessage(handler.obtainMessage( - MESSAGE_FINISH, success ? 1 : 0, 0, null - )); - } - } - }); - downloader.setPriority(Thread.MIN_PRIORITY); - downloader.start(); - } + private final ZLNetworkContext myNetworkContext = new ServiceNetworkContext(this); + + public interface Key { + String FROM_SYNC = "fbreader.downloader.from.sync"; + String BOOK_TITLE = "fbreader.downloader.book.title"; + String BOOK_KIND = "fbreader.downloader.book.kind"; + String BOOK_MIME = "fbreader.downloader.book.mime"; + String CLEAN_URL = "fbreader.downloader.clean.url"; + String SHOW_NOTIFICATIONS = "fbreader.downloader.show.notifications"; + String NOTIFICATION_TO_DISMISS_ID = "fbreader.downloader.notification.id"; + } + + public interface Notifications { + int DOWNLOAD_STARTED = 0x0001; + int ALREADY_IN_PROGRESS = 0x0002; + + int ALL = 0x0003; + } + + private Set myDownloadingURLs = Collections.synchronizedSet(new HashSet()); + private Set myOngoingNotifications = new HashSet(); + + private volatile int myServiceCounter; + + private void doStart() { + ++myServiceCounter; + } + + private void doStop() { + if (--myServiceCounter == 0) { + stopSelf(); + } + } + + public static ZLResource getResource() { + return ZLResource.resource("bookDownloader"); + } + + @Override + public IBinder onBind(Intent intent) { + return new BookDownloaderInterface.Stub() { + public boolean isBeingDownloaded(String url) { + return myDownloadingURLs.contains(url); + } + }; + } + + @Override + public void onDestroy() { + for (int notificationId : myOngoingNotifications) { + NotificationUtil.drop(this, notificationId); + } + myOngoingNotifications.clear(); + super.onDestroy(); + } + + @Override + public void onStart(Intent intent, int startId) { + super.onStart(intent, startId); + doStart(); + + final Uri uri = intent != null ? intent.getData() : null; + if (uri == null) { + doStop(); + return; + } + intent.setData(null); + + if (intent.getBooleanExtra(Key.FROM_SYNC, false)) { + final int notificationId = intent.getIntExtra( + Key.NOTIFICATION_TO_DISMISS_ID, + NotificationUtil.MISSING_BOOK_ID + ); + NotificationUtil.drop(this, notificationId); + } + + final int notifications = intent.getIntExtra(Key.SHOW_NOTIFICATIONS, 0); + + final String url = uri.toString(); + final MimeType mime = MimeType.get(intent.getStringExtra(Key.BOOK_MIME)); + UrlInfo.Type referenceType = (UrlInfo.Type) intent.getSerializableExtra(Key.BOOK_KIND); + if (referenceType == null) { + referenceType = UrlInfo.Type.Book; + } + + String cleanURL = intent.getStringExtra(Key.CLEAN_URL); + if (cleanURL == null) { + cleanURL = url; + } + + if (myDownloadingURLs.contains(url)) { + if ((notifications & Notifications.ALREADY_IN_PROGRESS) != 0) { + showMessage("alreadyDownloading"); + } + doStop(); + return; + } + + final String fileName = BookUrlInfo.makeBookFileName(cleanURL, mime, referenceType); + if (fileName == null) { + doStop(); + return; + } + + int index = fileName.lastIndexOf(File.separator); + if (index != -1) { + final String dir = fileName.substring(0, index); + final File dirFile = new File(dir); + if (!dirFile.exists() && !dirFile.mkdirs()) { + showMessage("cannotCreateDirectory", dirFile.getPath()); + doStop(); + return; + } + if (!dirFile.exists() || !dirFile.isDirectory()) { + showMessage("cannotCreateDirectory", dirFile.getPath()); + doStop(); + return; + } + } + + final File fileFile = new File(fileName); + if (fileFile.exists()) { + if (!fileFile.isFile()) { + showMessage("cannotCreateFile", fileFile.getPath()); + doStop(); + return; + } + // TODO: question box: redownload? + doStop(); + startActivity(getFBReaderIntent(fileFile)); + return; + } + String title = intent.getStringExtra(Key.BOOK_TITLE); + if (title == null || title.length() == 0) { + title = fileFile.getName(); + } + if ((notifications & Notifications.DOWNLOAD_STARTED) != 0) { + showMessage("downloadStarted"); + } + startFileDownload(url, fileFile, title); + } + + private void showMessageText(String text) { + Toast.makeText( + getApplicationContext(), + text, + Toast.LENGTH_LONG + ).show(); + } + + private void showMessage(String key) { + showMessageText(getResource().getResource(key).getValue()); + } + + private void showMessage(String key, String parameter) { + showMessageText(getResource().getResource(key).getValue().replace("%s", parameter)); + } + + private Intent getFBReaderIntent(final File file) { + final Intent intent = new Intent(getApplicationContext(), FBReader.class); + if (file != null) { + intent.setAction(Intent.ACTION_VIEW).setData(Uri.fromFile(file)); + } + return intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + } + + private Notification createDownloadFinishNotification(File file, String title, boolean success) { + final ZLResource resource = getResource(); + final String tickerText = success ? + resource.getResource("downloadCompleteTicker").getValue() : + resource.getResource("downloadFailedTicker").getValue(); + final String contentText = success ? + resource.getResource("downloadComplete").getValue() : + resource.getResource("downloadFailed").getValue(); + final Notification notification = new Notification( + android.R.drawable.stat_sys_download_done, + tickerText, + System.currentTimeMillis() + ); + notification.flags |= Notification.FLAG_AUTO_CANCEL; + final Intent intent = success ? getFBReaderIntent(file) : new Intent(); + final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0); + Notification.Builder builder = new Notification.Builder(getApplicationContext()); +// notification.setLatestEventInfo(getApplicationContext(), title, contentText, contentIntent); + builder.setContentTitle(title); + builder.setContentIntent(contentIntent); + builder.setContentText(contentText); + return builder.build(); + } + + private Notification createDownloadProgressNotification(String title) { + final RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.download_notification); + contentView.setTextViewText(R.id.download_notification_title, title); + contentView.setTextViewText(R.id.download_notification_progress_text, ""); + contentView.setProgressBar(R.id.download_notification_progress_bar, 100, 0, true); + + final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(), 0); + + final Notification notification = new Notification(); + notification.icon = android.R.drawable.stat_sys_download; + notification.flags |= Notification.FLAG_ONGOING_EVENT; + notification.contentView = contentView; + notification.contentIntent = contentIntent; + + return notification; + } + + private void sendDownloaderCallback() { + sendBroadcast( + new Intent(getApplicationContext(), ListenerCallback.class) + ); + } + + private void startFileDownload(final String urlString, final File file, final String title) { + myDownloadingURLs.add(urlString); + sendDownloaderCallback(); + + final int notificationId = NotificationUtil.getDownloadId(file.getPath()); + final Notification progressNotification = createDownloadProgressNotification(title); + + final NotificationManager notificationManager = + (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + myOngoingNotifications.add(Integer.valueOf(notificationId)); + notificationManager.notify(notificationId, progressNotification); + + final int MESSAGE_PROGRESS = 0; + final int MESSAGE_FINISH = 1; + + final Handler handler = new Handler() { + public void handleMessage(Message message) { + switch (message.what) { + case MESSAGE_PROGRESS: { + final int progress = message.arg1; + final RemoteViews contentView = (RemoteViews) progressNotification.contentView; + final boolean showPercent = progress >= 0; + contentView.setTextViewText( + R.id.download_notification_progress_text, + showPercent ? progress + "%" : "" + ); + contentView.setProgressBar( + R.id.download_notification_progress_bar, + 100, showPercent ? progress : 0, !showPercent + ); + notificationManager.notify(notificationId, progressNotification); + break; + } + case MESSAGE_FINISH: + myDownloadingURLs.remove(urlString); + NotificationUtil.drop(BookDownloaderService.this, notificationId); + myOngoingNotifications.remove(Integer.valueOf(notificationId)); + notificationManager.notify( + notificationId, + createDownloadFinishNotification(file, title, message.arg1 != 0) + ); + sendDownloaderCallback(); + doStop(); + break; + } + } + }; + + final ZLNetworkRequest request = new ZLNetworkRequest.Get(urlString) { + public void handleStream(InputStream inputStream, int length) throws IOException, ZLNetworkException { + final int updateIntervalMillis = 1000; // FIXME: remove hardcoded time constant + + int downloadedPart = 0; + long progressTime = System.currentTimeMillis() + updateIntervalMillis; + if (length <= 0) { + handler.sendMessage(handler.obtainMessage(MESSAGE_PROGRESS, -1, 0, null)); + } + final OutputStream outStream; + try { + outStream = new FileOutputStream(file); + } catch (FileNotFoundException ex) { + throw ZLNetworkException.forCode(ZLNetworkException.ERROR_CREATE_FILE, file.getPath()); + } + try { + final byte[] buffer = new byte[8192]; + while (true) { + final int size = inputStream.read(buffer); + if (size <= 0) { + break; + } + downloadedPart += size; + if (length > 0) { + final long currentTime = System.currentTimeMillis(); + if (currentTime > progressTime) { + progressTime = currentTime + updateIntervalMillis; + handler.sendMessage(handler.obtainMessage( + MESSAGE_PROGRESS, downloadedPart * 100 / length, 0, null + )); + } + } + outStream.write(buffer, 0, size); + } + final BookCollectionShadow collection = new BookCollectionShadow(); + collection.bindToService(BookDownloaderService.this, new Runnable() { + @Override + public void run() { + collection.rescan(file.getPath()); + collection.unbind(); + } + }); + } finally { + outStream.close(); + } + } + }; + + final Thread downloader = new Thread(new Runnable() { + public void run() { + boolean success = false; + try { + SQLiteCookieDatabase.init(BookDownloaderService.this); + myNetworkContext.perform(request); + success = true; + } catch (final ZLNetworkException e) { + e.printStackTrace(); + final String title = getResource().getResource("downloadFailed").getValue(); + handler.post(new Runnable() { + public void run() { + showMessageText(title + ": " + e.getMessage()); + } + }); + file.delete(); + } finally { + handler.sendMessage(handler.obtainMessage( + MESSAGE_FINISH, success ? 1 : 0, 0, null + )); + } + } + }); + downloader.setPriority(Thread.MIN_PRIORITY); + downloader.start(); + } } diff --git a/fBReader/src/main/java/org/geometerplus/fbreader/fbreader/ActionCode.java b/fBReader/src/main/java/org/geometerplus/fbreader/fbreader/ActionCode.java index d3baa18..220e848 100644 --- a/fBReader/src/main/java/org/geometerplus/fbreader/fbreader/ActionCode.java +++ b/fBReader/src/main/java/org/geometerplus/fbreader/fbreader/ActionCode.java @@ -51,6 +51,7 @@ public interface ActionCode { String VOLUME_KEY_SCROLL_FORWARD = "volumeKeyScrollForward"; String VOLUME_KEY_SCROLL_BACK = "volumeKeyScrollBackward"; String SHOW_MENU = "menu"; + String SHOW_TRANSLATE = "translate"; String SHOW_NAVIGATION = "navigate"; String GO_BACK = "goBack"; diff --git a/fBReader/src/main/java/org/geometerplus/fbreader/fbreader/FBView.java b/fBReader/src/main/java/org/geometerplus/fbreader/fbreader/FBView.java index 724f4f0..5af44b6 100644 --- a/fBReader/src/main/java/org/geometerplus/fbreader/fbreader/FBView.java +++ b/fBReader/src/main/java/org/geometerplus/fbreader/fbreader/FBView.java @@ -19,8 +19,17 @@ package org.geometerplus.fbreader.fbreader; -import java.util.*; - +import org.geometerplus.fbreader.bookmodel.BookModel; +import org.geometerplus.fbreader.bookmodel.FBHyperlinkType; +import org.geometerplus.fbreader.bookmodel.TOCTree; +import org.geometerplus.fbreader.fbreader.options.ColorProfile; +import org.geometerplus.fbreader.fbreader.options.FooterOptions; +import org.geometerplus.fbreader.fbreader.options.ImageOptions; +import org.geometerplus.fbreader.fbreader.options.MiscOptions; +import org.geometerplus.fbreader.fbreader.options.PageTurningOptions; +import org.geometerplus.fbreader.fbreader.options.ViewOptions; +import org.geometerplus.fbreader.util.FixedTextSnippet; +import org.geometerplus.fbreader.util.TextSnippet; import org.geometerplus.zlibrary.core.filesystem.ZLFile; import org.geometerplus.zlibrary.core.filesystem.ZLResourceFile; import org.geometerplus.zlibrary.core.fonts.FontEntry; @@ -28,768 +37,787 @@ import org.geometerplus.zlibrary.core.util.ZLColor; import org.geometerplus.zlibrary.core.view.SelectionCursor; import org.geometerplus.zlibrary.core.view.ZLPaintContext; - import org.geometerplus.zlibrary.text.model.ZLTextModel; -import org.geometerplus.zlibrary.text.view.*; +import org.geometerplus.zlibrary.text.view.ExtensionElementManager; +import org.geometerplus.zlibrary.text.view.ZLTextHighlighting; +import org.geometerplus.zlibrary.text.view.ZLTextHyperlink; +import org.geometerplus.zlibrary.text.view.ZLTextHyperlinkRegionSoul; +import org.geometerplus.zlibrary.text.view.ZLTextImageRegionSoul; +import org.geometerplus.zlibrary.text.view.ZLTextPosition; +import org.geometerplus.zlibrary.text.view.ZLTextRegion; +import org.geometerplus.zlibrary.text.view.ZLTextVideoRegionSoul; +import org.geometerplus.zlibrary.text.view.ZLTextView; +import org.geometerplus.zlibrary.text.view.ZLTextWordRegionSoul; import org.geometerplus.zlibrary.text.view.style.ZLTextStyleCollection; -import org.geometerplus.fbreader.bookmodel.BookModel; -import org.geometerplus.fbreader.bookmodel.FBHyperlinkType; -import org.geometerplus.fbreader.bookmodel.TOCTree; -import org.geometerplus.fbreader.fbreader.options.*; -import org.geometerplus.fbreader.util.FixedTextSnippet; -import org.geometerplus.fbreader.util.TextSnippet; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; public final class FBView extends ZLTextView { - private final FBReaderApp myReader; - private final ViewOptions myViewOptions; - private final BookElementManager myBookElementManager; - - FBView(FBReaderApp reader) { - super(reader); - myReader = reader; - myViewOptions = reader.ViewOptions; - myBookElementManager = new BookElementManager(this); - } - - public void setModel(ZLTextModel model) { - super.setModel(model); - if (myFooter != null) { - myFooter.resetTOCMarks(); - } - } - - private int myStartY; - private boolean myIsBrightnessAdjustmentInProgress; - private int myStartBrightness; - - private TapZoneMap myZoneMap; - - private TapZoneMap getZoneMap() { - final PageTurningOptions prefs = myReader.PageTurningOptions; - String id = prefs.TapZoneMap.getValue(); - if ("".equals(id)) { - id = prefs.Horizontal.getValue() ? "right_to_left" : "up"; - } - if (myZoneMap == null || !id.equals(myZoneMap.Name)) { - myZoneMap = TapZoneMap.zoneMap(id); - } - return myZoneMap; - } - - private void onFingerSingleTapLastResort(int x, int y) { - myReader.runAction(getZoneMap().getActionByCoordinates( - x, y, getContextWidth(), getContextHeight(), - isDoubleTapSupported() ? TapZoneMap.Tap.singleNotDoubleTap : TapZoneMap.Tap.singleTap - ), x, y); - } - - @Override - public void onFingerSingleTap(int x, int y) { - final ZLTextRegion hyperlinkRegion = findRegion(x, y, maxSelectionDistance(), ZLTextRegion.HyperlinkFilter); - if (hyperlinkRegion != null) { - outlineRegion(hyperlinkRegion); - myReader.getViewWidget().reset(); - myReader.getViewWidget().repaint(); - myReader.runAction(ActionCode.PROCESS_HYPERLINK); - return; - } - - final ZLTextRegion bookRegion = findRegion(x, y, 0, ZLTextRegion.ExtensionFilter); - if (bookRegion != null) { - myReader.runAction(ActionCode.DISPLAY_BOOK_POPUP, bookRegion); - return; - } - - final ZLTextRegion videoRegion = findRegion(x, y, 0, ZLTextRegion.VideoFilter); - if (videoRegion != null) { - outlineRegion(videoRegion); - myReader.getViewWidget().reset(); - myReader.getViewWidget().repaint(); - myReader.runAction(ActionCode.OPEN_VIDEO, (ZLTextVideoRegionSoul)videoRegion.getSoul()); - return; - } - - final ZLTextHighlighting highlighting = findHighlighting(x, y, maxSelectionDistance()); - if (highlighting instanceof BookmarkHighlighting) { - myReader.runAction( - ActionCode.SELECTION_BOOKMARK, - ((BookmarkHighlighting)highlighting).Bookmark - ); - return; - } - - if (myReader.isActionEnabled(ActionCode.HIDE_TOAST)) { - myReader.runAction(ActionCode.HIDE_TOAST); - return; - } - - onFingerSingleTapLastResort(x, y); - } - - @Override - public boolean isDoubleTapSupported() { - return myReader.MiscOptions.EnableDoubleTap.getValue(); - } - - @Override - public void onFingerDoubleTap(int x, int y) { - myReader.runAction(ActionCode.HIDE_TOAST); - - myReader.runAction(getZoneMap().getActionByCoordinates( - x, y, getContextWidth(), getContextHeight(), TapZoneMap.Tap.doubleTap - ), x, y); - } - - @Override - public void onFingerPress(int x, int y) { - myReader.runAction(ActionCode.HIDE_TOAST); - - final float maxDist = ZLibrary.Instance().getDisplayDPI() / 4; - final SelectionCursor.Which cursor = findSelectionCursor(x, y, maxDist * maxDist); - if (cursor != null) { - myReader.runAction(ActionCode.SELECTION_HIDE_PANEL); - moveSelectionCursorTo(cursor, x, y); - return; - } - - if (myReader.MiscOptions.AllowScreenBrightnessAdjustment.getValue() && x < getContextWidth() / 10) { - myIsBrightnessAdjustmentInProgress = true; - myStartY = y; - myStartBrightness = myReader.getViewWidget().getScreenBrightness(); - return; - } - - startManualScrolling(x, y); - } - - private boolean isFlickScrollingEnabled() { - final PageTurningOptions.FingerScrollingType fingerScrolling = - myReader.PageTurningOptions.FingerScrolling.getValue(); - return - fingerScrolling == PageTurningOptions.FingerScrollingType.byFlick || - fingerScrolling == PageTurningOptions.FingerScrollingType.byTapAndFlick; - } - - private void startManualScrolling(int x, int y) { - if (!isFlickScrollingEnabled()) { - return; - } - - final boolean horizontal = myReader.PageTurningOptions.Horizontal.getValue(); - final Direction direction = horizontal ? Direction.rightToLeft : Direction.up; - myReader.getViewWidget().startManualScrolling(x, y, direction); - } - - @Override - public void onFingerMove(int x, int y) { - final SelectionCursor.Which cursor = getSelectionCursorInMovement(); - if (cursor != null) { - moveSelectionCursorTo(cursor, x, y); - return; - } - - synchronized (this) { - if (myIsBrightnessAdjustmentInProgress) { - if (x >= getContextWidth() / 5) { - myIsBrightnessAdjustmentInProgress = false; - startManualScrolling(x, y); - } else { - final int delta = (myStartBrightness + 30) * (myStartY - y) / getContextHeight(); - myReader.getViewWidget().setScreenBrightness(myStartBrightness + delta); - return; - } - } - - if (isFlickScrollingEnabled()) { - myReader.getViewWidget().scrollManuallyTo(x, y); - } - } - } - - @Override - public void onFingerRelease(int x, int y) { - final SelectionCursor.Which cursor = getSelectionCursorInMovement(); - if (cursor != null) { - releaseSelectionCursor(); - } else if (myIsBrightnessAdjustmentInProgress) { - myIsBrightnessAdjustmentInProgress = false; - } else if (isFlickScrollingEnabled()) { - myReader.getViewWidget().startAnimatedScrolling( - x, y, myReader.PageTurningOptions.AnimationSpeed.getValue() - ); - } - } - - @Override - public boolean onFingerLongPress(int x, int y) { - myReader.runAction(ActionCode.HIDE_TOAST); - - final ZLTextRegion region = findRegion(x, y, maxSelectionDistance(), ZLTextRegion.AnyRegionFilter); - if (region != null) { - final ZLTextRegion.Soul soul = region.getSoul(); - boolean doSelectRegion = false; - if (soul instanceof ZLTextWordRegionSoul) { - switch (myReader.MiscOptions.WordTappingAction.getValue()) { - case startSelecting: - myReader.runAction(ActionCode.SELECTION_HIDE_PANEL); - initSelection(x, y); - final SelectionCursor.Which cursor = findSelectionCursor(x, y); - if (cursor != null) { - moveSelectionCursorTo(cursor, x, y); - } - return true; - case selectSingleWord: - case openDictionary: - doSelectRegion = true; - break; - } - } else if (soul instanceof ZLTextImageRegionSoul) { - doSelectRegion = - myReader.ImageOptions.TapAction.getValue() != - ImageOptions.TapActionEnum.doNothing; - } else if (soul instanceof ZLTextHyperlinkRegionSoul) { - doSelectRegion = true; - } - - if (doSelectRegion) { - outlineRegion(region); - myReader.getViewWidget().reset(); - myReader.getViewWidget().repaint(); - return true; - } - } - return false; - } - - @Override - public void onFingerMoveAfterLongPress(int x, int y) { - final SelectionCursor.Which cursor = getSelectionCursorInMovement(); - if (cursor != null) { - moveSelectionCursorTo(cursor, x, y); - return; - } - - ZLTextRegion region = getOutlinedRegion(); - if (region != null) { - ZLTextRegion.Soul soul = region.getSoul(); - if (soul instanceof ZLTextHyperlinkRegionSoul || - soul instanceof ZLTextWordRegionSoul) { - if (myReader.MiscOptions.WordTappingAction.getValue() != - MiscOptions.WordTappingActionEnum.doNothing) { - region = findRegion(x, y, maxSelectionDistance(), ZLTextRegion.AnyRegionFilter); - if (region != null) { - soul = region.getSoul(); - if (soul instanceof ZLTextHyperlinkRegionSoul - || soul instanceof ZLTextWordRegionSoul) { - outlineRegion(region); - myReader.getViewWidget().reset(); - myReader.getViewWidget().repaint(); - } - } - } - } - } - } - - @Override - public void onFingerReleaseAfterLongPress(int x, int y) { - final SelectionCursor.Which cursor = getSelectionCursorInMovement(); - if (cursor != null) { - releaseSelectionCursor(); - return; - } - - final ZLTextRegion region = getOutlinedRegion(); - if (region != null) { - final ZLTextRegion.Soul soul = region.getSoul(); - - boolean doRunAction = false; - if (soul instanceof ZLTextWordRegionSoul) { - doRunAction = - myReader.MiscOptions.WordTappingAction.getValue() == - MiscOptions.WordTappingActionEnum.openDictionary; - } else if (soul instanceof ZLTextImageRegionSoul) { - doRunAction = - myReader.ImageOptions.TapAction.getValue() == - ImageOptions.TapActionEnum.openImageView; - } - - if (doRunAction) { - myReader.runAction(ActionCode.PROCESS_HYPERLINK); - } - } - } - - @Override - public void onFingerEventCancelled() { - final SelectionCursor.Which cursor = getSelectionCursorInMovement(); - if (cursor != null) { - releaseSelectionCursor(); - } - } - - public boolean onTrackballRotated(int diffX, int diffY) { - if (diffX == 0 && diffY == 0) { - return true; - } - - final Direction direction = (diffY != 0) ? - (diffY > 0 ? Direction.down : Direction.up) : - (diffX > 0 ? Direction.leftToRight : Direction.rightToLeft); - - new MoveCursorAction(myReader, direction).run(); - return true; - } - - @Override - public ZLTextStyleCollection getTextStyleCollection() { - return myViewOptions.getTextStyleCollection(); - } - - @Override - public ImageFitting getImageFitting() { - return myReader.ImageOptions.FitToScreen.getValue(); - } - - @Override - public int getLeftMargin() { - return myViewOptions.LeftMargin.getValue(); - } - - @Override - public int getRightMargin() { - return myViewOptions.RightMargin.getValue(); - } - - @Override - public int getTopMargin() { - return myViewOptions.TopMargin.getValue(); - } - - @Override - public int getBottomMargin() { - return myViewOptions.BottomMargin.getValue(); - } - - @Override - public int getSpaceBetweenColumns() { - return myViewOptions.SpaceBetweenColumns.getValue(); - } - - @Override - public boolean twoColumnView() { - return getContextHeight() <= getContextWidth() && myViewOptions.TwoColumnView.getValue(); - } - - @Override - public ZLFile getWallpaperFile() { - final String filePath = myViewOptions.getColorProfile().WallpaperOption.getValue(); - if ("".equals(filePath)) { - return null; - } - - final ZLFile file = ZLFile.createFileByPath(filePath); - if (file == null || !file.exists()) { - return null; - } - return file; - } - - @Override - public ZLPaintContext.FillMode getFillMode() { - return getWallpaperFile() instanceof ZLResourceFile - ? ZLPaintContext.FillMode.tileMirror - : myViewOptions.getColorProfile().FillModeOption.getValue(); - } - - @Override - public ZLColor getBackgroundColor() { - return myViewOptions.getColorProfile().BackgroundOption.getValue(); - } - - @Override - public ZLColor getSelectionBackgroundColor() { - return myViewOptions.getColorProfile().SelectionBackgroundOption.getValue(); - } - - @Override - public ZLColor getSelectionForegroundColor() { - return myViewOptions.getColorProfile().SelectionForegroundOption.getValue(); - } - - @Override - public ZLColor getTextColor(ZLTextHyperlink hyperlink) { - final ColorProfile profile = myViewOptions.getColorProfile(); - switch (hyperlink.Type) { - default: - case FBHyperlinkType.NONE: - return profile.RegularTextOption.getValue(); - case FBHyperlinkType.INTERNAL: - case FBHyperlinkType.FOOTNOTE: - return myReader.Collection.isHyperlinkVisited(myReader.getCurrentBook(), hyperlink.Id) - ? profile.VisitedHyperlinkTextOption.getValue() - : profile.HyperlinkTextOption.getValue(); - case FBHyperlinkType.EXTERNAL: - return profile.HyperlinkTextOption.getValue(); - } - } - - @Override - public ZLColor getHighlightingBackgroundColor() { - return myViewOptions.getColorProfile().HighlightingBackgroundOption.getValue(); - } - - @Override - public ZLColor getHighlightingForegroundColor() { - return myViewOptions.getColorProfile().HighlightingForegroundOption.getValue(); - } - - private abstract class Footer implements FooterArea { - private Runnable UpdateTask = new Runnable() { - public void run() { - myReader.getViewWidget().repaint(); - } - }; - - protected ArrayList myTOCMarks; - private int myMaxTOCMarksNumber = -1; - - public int getHeight() { - return myViewOptions.FooterHeight.getValue(); - } - - public synchronized void resetTOCMarks() { - myTOCMarks = null; - } - - protected synchronized void updateTOCMarks(BookModel model, int maxNumber) { - if (myTOCMarks != null && myMaxTOCMarksNumber == maxNumber) { - return; - } - - myTOCMarks = new ArrayList(); - myMaxTOCMarksNumber = maxNumber; - - TOCTree toc = model.TOCTree; - if (toc == null) { - return; - } - int maxLevel = Integer.MAX_VALUE; - if (toc.getSize() >= maxNumber) { - final int[] sizes = new int[10]; - for (TOCTree tocItem : toc) { - if (tocItem.Level < 10) { - ++sizes[tocItem.Level]; - } - } - for (int i = 1; i < sizes.length; ++i) { - sizes[i] += sizes[i - 1]; - } - for (maxLevel = sizes.length - 1; maxLevel >= 0; --maxLevel) { - if (sizes[maxLevel] < maxNumber) { - break; - } - } - } - for (TOCTree tocItem : toc.allSubtrees(maxLevel)) { - myTOCMarks.add(tocItem); - } - } - - protected String buildInfoString(PagePosition pagePosition, String separator) { - final StringBuilder info = new StringBuilder(); - final FooterOptions footerOptions = myViewOptions.getFooterOptions(); - - if (footerOptions.showProgressAsPages()) { - maybeAddSeparator(info, separator); - info.append(pagePosition.Current); - info.append("/"); - info.append(pagePosition.Total); - } - if (footerOptions.showProgressAsPercentage() && pagePosition.Total != 0) { - maybeAddSeparator(info, separator); - info.append(String.valueOf(100 * pagePosition.Current / pagePosition.Total)); - info.append("%"); - } - - if (footerOptions.ShowClock.getValue()) { - maybeAddSeparator(info, separator); - info.append(ZLibrary.Instance().getCurrentTimeString()); - } - if (footerOptions.ShowBattery.getValue()) { - maybeAddSeparator(info, separator); - info.append(myReader.getBatteryLevel()); - info.append("%"); - } - return info.toString(); - } - - private void maybeAddSeparator(StringBuilder info, String separator) { - if (info.length() > 0) { - info.append(separator); - } - } - - private List myFontEntry; - private Map myHeightMap = new HashMap(); - private Map myCharHeightMap = new HashMap(); - protected synchronized int setFont(ZLPaintContext context, int height, boolean bold) { - final String family = myViewOptions.getFooterOptions().Font.getValue(); - if (myFontEntry == null || !family.equals(myFontEntry.get(0).Family)) { - myFontEntry = Collections.singletonList(FontEntry.systemEntry(family)); - } - final String key = family + (bold ? "N" : "B") + height; - final Integer cached = myHeightMap.get(key); - if (cached != null) { - context.setFont(myFontEntry, cached, bold, false, false, false); - final Integer charHeight = myCharHeightMap.get(key); - return charHeight != null ? charHeight : height; - } else { - int h = height + 2; - int charHeight = height; - final int max = height < 9 ? height - 1 : height - 2; - for (; h > 5; --h) { - context.setFont(myFontEntry, h, bold, false, false, false); - charHeight = context.getCharHeight('H'); - if (charHeight <= max) { - break; - } - } - myHeightMap.put(key, h); - myCharHeightMap.put(key, charHeight); - return charHeight; - } - } - } - - private class FooterOldStyle extends Footer { - public synchronized void paint(ZLPaintContext context) { - final ZLFile wallpaper = getWallpaperFile(); - if (wallpaper != null) { - context.clear(wallpaper, getFillMode()); - } else { - context.clear(getBackgroundColor()); - } - - final BookModel model = myReader.Model; - if (model == null) { - return; - } - - //final ZLColor bgColor = getBackgroundColor(); - // TODO: separate color option for footer color - final ZLColor fgColor = getTextColor(ZLTextHyperlink.NO_LINK); - final ZLColor fillColor = myViewOptions.getColorProfile().FooterFillOption.getValue(); - - final int left = getLeftMargin(); - final int right = context.getWidth() - getRightMargin(); - final int height = getHeight(); - final int lineWidth = height <= 10 ? 1 : 2; - final int delta = height <= 10 ? 0 : 1; - setFont(context, height, height > 10); - - final PagePosition pagePosition = FBView.this.pagePosition(); - - // draw info text - final String infoString = buildInfoString(pagePosition, " "); - final int infoWidth = context.getStringWidth(infoString); - context.setTextColor(fgColor); - context.drawString(right - infoWidth, height - delta, infoString); - - // draw gauge - final int gaugeRight = right - (infoWidth == 0 ? 0 : infoWidth + 10); - final int gaugeWidth = gaugeRight - left - 2 * lineWidth; - - context.setLineColor(fgColor); - context.setLineWidth(lineWidth); - context.drawLine(left, lineWidth, left, height - lineWidth); - context.drawLine(left, height - lineWidth, gaugeRight, height - lineWidth); - context.drawLine(gaugeRight, height - lineWidth, gaugeRight, lineWidth); - context.drawLine(gaugeRight, lineWidth, left, lineWidth); - - final int gaugeInternalRight = - left + lineWidth + (int)(1.0 * gaugeWidth * pagePosition.Current / pagePosition.Total); - - context.setFillColor(fillColor); - context.fillRectangle(left + 1, height - 2 * lineWidth, gaugeInternalRight, lineWidth + 1); - - final FooterOptions footerOptions = myViewOptions.getFooterOptions(); - if (footerOptions.ShowTOCMarks.getValue()) { - updateTOCMarks(model, footerOptions.MaxTOCMarks.getValue()); - final int fullLength = sizeOfFullText(); - for (TOCTree tocItem : myTOCMarks) { - TOCTree.Reference reference = tocItem.getReference(); - if (reference != null) { - final int refCoord = sizeOfTextBeforeParagraph(reference.ParagraphIndex); - final int xCoord = - left + 2 * lineWidth + (int)(1.0 * gaugeWidth * refCoord / fullLength); - context.drawLine(xCoord, height - lineWidth, xCoord, lineWidth); - } - } - } - } - } - - private class FooterNewStyle extends Footer { - public synchronized void paint(ZLPaintContext context) { - final ColorProfile cProfile = myViewOptions.getColorProfile(); - context.clear(cProfile.FooterNGBackgroundOption.getValue()); - - final BookModel model = myReader.Model; - if (model == null) { - return; - } - - final ZLColor textColor = cProfile.FooterNGForegroundOption.getValue(); - final ZLColor readColor = cProfile.FooterNGForegroundOption.getValue(); - final ZLColor unreadColor = cProfile.FooterNGForegroundUnreadOption.getValue(); - - final int left = getLeftMargin(); - final int right = context.getWidth() - getRightMargin(); - final int height = getHeight(); - final int lineWidth = height <= 12 ? 1 : 2; - final int charHeight = setFont(context, height, height > 12); - - final PagePosition pagePosition = FBView.this.pagePosition(); - - // draw info text - final String infoString = buildInfoString(pagePosition, " "); - final int infoWidth = context.getStringWidth(infoString); - context.setTextColor(textColor); - context.drawString(right - infoWidth, (height + charHeight + 1) / 2, infoString); - - // draw gauge - final int gaugeRight = right - (infoWidth == 0 ? 0 : infoWidth + 10); - final int gaugeInternalRight = - left + (int)(1.0 * (gaugeRight - left) * pagePosition.Current / pagePosition.Total + 0.5); - final int v = height / 2; - - context.setLineWidth(lineWidth); - context.setLineColor(readColor); - context.drawLine(left, v, gaugeInternalRight, v); - if (gaugeInternalRight < gaugeRight) { - context.setLineColor(unreadColor); - context.drawLine(gaugeInternalRight + 1, v, gaugeRight, v); - } - - // draw labels - final FooterOptions footerOptions = myViewOptions.getFooterOptions(); - if (footerOptions.ShowTOCMarks.getValue()) { - final TreeSet labels = new TreeSet(); - labels.add(left); - labels.add(gaugeRight); - updateTOCMarks(model, footerOptions.MaxTOCMarks.getValue()); - final int fullLength = sizeOfFullText(); - for (TOCTree tocItem : myTOCMarks) { - TOCTree.Reference reference = tocItem.getReference(); - if (reference != null) { - final int refCoord = sizeOfTextBeforeParagraph(reference.ParagraphIndex); - labels.add(left + (int)(1.0 * (gaugeRight - left) * refCoord / fullLength + 0.5)); - } - } - for (int l : labels) { - context.setLineColor(l <= gaugeInternalRight ? readColor : unreadColor); - context.drawLine(l, v + 3, l, v - lineWidth - 2); - } - } - } - } - - private Footer myFooter; - - @Override - public Footer getFooterArea() { - switch (myViewOptions.ScrollbarType.getValue()) { - case SCROLLBAR_SHOW_AS_FOOTER: - if (!(myFooter instanceof FooterNewStyle)) { - if (myFooter != null) { - myReader.removeTimerTask(myFooter.UpdateTask); - } - myFooter = new FooterNewStyle(); - myReader.addTimerTask(myFooter.UpdateTask, 15000); - } - break; - case SCROLLBAR_SHOW_AS_FOOTER_OLD_STYLE: - if (!(myFooter instanceof FooterOldStyle)) { - if (myFooter != null) { - myReader.removeTimerTask(myFooter.UpdateTask); - } - myFooter = new FooterOldStyle(); - myReader.addTimerTask(myFooter.UpdateTask, 15000); - } - break; - default: - if (myFooter != null) { - myReader.removeTimerTask(myFooter.UpdateTask); - myFooter = null; - } - break; - } - return myFooter; - } - - @Override - protected void releaseSelectionCursor() { - super.releaseSelectionCursor(); - if (getCountOfSelectedWords() > 0) { - myReader.runAction(ActionCode.SELECTION_SHOW_PANEL); - } - } - - public TextSnippet getSelectedSnippet() { - final ZLTextPosition start = getSelectionStartPosition(); - final ZLTextPosition end = getSelectionEndPosition(); - if (start == null || end == null) { - return null; - } - final TextBuildTraverser traverser = new TextBuildTraverser(this); - traverser.traverse(start, end); - return new FixedTextSnippet(start, end, traverser.getText()); - } - - public int getCountOfSelectedWords() { - final WordCountTraverser traverser = new WordCountTraverser(this); - if (!isSelectionEmpty()) { - traverser.traverse(getSelectionStartPosition(), getSelectionEndPosition()); - } - return traverser.getCount(); - } - - public static final int SCROLLBAR_SHOW_AS_FOOTER = 3; - public static final int SCROLLBAR_SHOW_AS_FOOTER_OLD_STYLE = 4; - - @Override - public int scrollbarType() { - return myViewOptions.ScrollbarType.getValue(); - } - - @Override - public Animation getAnimationType() { - return myReader.PageTurningOptions.Animation.getValue(); - } - - @Override - protected ZLPaintContext.ColorAdjustingMode getAdjustingModeForImages() { - if (myReader.ImageOptions.MatchBackground.getValue()) { - if (ColorProfile.DAY.equals(myViewOptions.getColorProfile().Name)) { - return ZLPaintContext.ColorAdjustingMode.DARKEN_TO_BACKGROUND; - } else { - return ZLPaintContext.ColorAdjustingMode.LIGHTEN_TO_BACKGROUND; - } - } else { - return ZLPaintContext.ColorAdjustingMode.NONE; - } - } - - @Override - public synchronized void onScrollingFinished(PageIndex pageIndex) { - super.onScrollingFinished(pageIndex); - myReader.storePosition(); - } - - @Override - protected ExtensionElementManager getExtensionManager() { - return myBookElementManager; - } + private final FBReaderApp myReader; + private final ViewOptions myViewOptions; + private final BookElementManager myBookElementManager; + + FBView(FBReaderApp reader) { + super(reader); + myReader = reader; + myViewOptions = reader.ViewOptions; + myBookElementManager = new BookElementManager(this); + } + + public void setModel(ZLTextModel model) { + super.setModel(model); + if (myFooter != null) { + myFooter.resetTOCMarks(); + } + } + + private int myStartY; + private boolean myIsBrightnessAdjustmentInProgress; + private int myStartBrightness; + + private TapZoneMap myZoneMap; + + private TapZoneMap getZoneMap() { + final PageTurningOptions prefs = myReader.PageTurningOptions; + String id = prefs.TapZoneMap.getValue(); + if ("".equals(id)) { + id = prefs.Horizontal.getValue() ? "right_to_left" : "up"; + } + if (myZoneMap == null || !id.equals(myZoneMap.Name)) { + myZoneMap = TapZoneMap.zoneMap(id); + } + return myZoneMap; + } + + private void onFingerSingleTapLastResort(int x, int y) { + myReader.runAction(getZoneMap().getActionByCoordinates( + x, y, getContextWidth(), getContextHeight(), + isDoubleTapSupported() ? TapZoneMap.Tap.singleNotDoubleTap : TapZoneMap.Tap.singleTap + ), x, y); + } + + @Override + public void onFingerSingleTap(int x, int y) { + final ZLTextRegion hyperlinkRegion = findRegion(x, y, maxSelectionDistance(), ZLTextRegion.HyperlinkFilter); + if (hyperlinkRegion != null) { + outlineRegion(hyperlinkRegion); + myReader.getViewWidget().reset(); + myReader.getViewWidget().repaint(); + myReader.runAction(ActionCode.PROCESS_HYPERLINK); + return; + } + + final ZLTextRegion bookRegion = findRegion(x, y, 0, ZLTextRegion.ExtensionFilter); + if (bookRegion != null) { + myReader.runAction(ActionCode.DISPLAY_BOOK_POPUP, bookRegion); + return; + } + + final ZLTextRegion videoRegion = findRegion(x, y, 0, ZLTextRegion.VideoFilter); + if (videoRegion != null) { + outlineRegion(videoRegion); + myReader.getViewWidget().reset(); + myReader.getViewWidget().repaint(); + myReader.runAction(ActionCode.OPEN_VIDEO, (ZLTextVideoRegionSoul) videoRegion.getSoul()); + return; + } + + final ZLTextHighlighting highlighting = findHighlighting(x, y, maxSelectionDistance()); + if (highlighting instanceof BookmarkHighlighting) { + myReader.runAction( + ActionCode.SELECTION_BOOKMARK, + ((BookmarkHighlighting) highlighting).Bookmark + ); + return; + } + final ZLTextRegion region = findRegion(x, y, maxSelectionDistance(), ZLTextRegion.AnyRegionFilter); + if (region != null) { + myReader.runAction(ActionCode.SHOW_TRANSLATE, ((ZLTextWordRegionSoul) region.getSoul()).Word.toString(), (region.getLeft() + region.getRight()) / 2, (region.getTop() + region.getBottom()) / 2); +// myReader.runAction(ActionCode.SHOW_TRANSLATE, ((ZLTextWordRegionSoul) region.getSoul()).Word.toString(), x,y); + return; + } + if (myReader.isActionEnabled(ActionCode.HIDE_TOAST)) { + myReader.runAction(ActionCode.HIDE_TOAST); + return; + } + + onFingerSingleTapLastResort(x, y); + } + + @Override + public boolean isDoubleTapSupported() { + return myReader.MiscOptions.EnableDoubleTap.getValue(); + } + + @Override + public void onFingerDoubleTap(int x, int y) { + myReader.runAction(ActionCode.HIDE_TOAST); + + myReader.runAction(getZoneMap().getActionByCoordinates( + x, y, getContextWidth(), getContextHeight(), TapZoneMap.Tap.doubleTap + ), x, y); + } + + @Override + public void onFingerPress(int x, int y) { + myReader.runAction(ActionCode.HIDE_TOAST); + + final float maxDist = ZLibrary.Instance().getDisplayDPI() / 4; + final SelectionCursor.Which cursor = findSelectionCursor(x, y, maxDist * maxDist); + if (cursor != null) { + myReader.runAction(ActionCode.SELECTION_HIDE_PANEL); + moveSelectionCursorTo(cursor, x, y); + return; + } + + if (myReader.MiscOptions.AllowScreenBrightnessAdjustment.getValue() && x < getContextWidth() / 10) { + myIsBrightnessAdjustmentInProgress = true; + myStartY = y; + myStartBrightness = myReader.getViewWidget().getScreenBrightness(); + return; + } + + startManualScrolling(x, y); + } + + private boolean isFlickScrollingEnabled() { + final PageTurningOptions.FingerScrollingType fingerScrolling = + myReader.PageTurningOptions.FingerScrolling.getValue(); + return + fingerScrolling == PageTurningOptions.FingerScrollingType.byFlick || + fingerScrolling == PageTurningOptions.FingerScrollingType.byTapAndFlick; + } + + private void startManualScrolling(int x, int y) { + if (!isFlickScrollingEnabled()) { + return; + } + + final boolean horizontal = myReader.PageTurningOptions.Horizontal.getValue(); + final Direction direction = horizontal ? Direction.rightToLeft : Direction.up; + myReader.getViewWidget().startManualScrolling(x, y, direction); + } + + @Override + public void onFingerMove(int x, int y) { + final SelectionCursor.Which cursor = getSelectionCursorInMovement(); + if (cursor != null) { + moveSelectionCursorTo(cursor, x, y); + return; + } + + synchronized (this) { + if (myIsBrightnessAdjustmentInProgress) { + if (x >= getContextWidth() / 5) { + myIsBrightnessAdjustmentInProgress = false; + startManualScrolling(x, y); + } else { + final int delta = (myStartBrightness + 30) * (myStartY - y) / getContextHeight(); + myReader.getViewWidget().setScreenBrightness(myStartBrightness + delta); + return; + } + } + + if (isFlickScrollingEnabled()) { + myReader.getViewWidget().scrollManuallyTo(x, y); + } + } + } + + @Override + public void onFingerUp(int x, int y) { + myReader.runAction(ActionCode.SHOW_MENU, x, y); + } + + @Override + public void onFingerRelease(int x, int y) { + final SelectionCursor.Which cursor = getSelectionCursorInMovement(); + if (cursor != null) { + releaseSelectionCursor(); + } else if (myIsBrightnessAdjustmentInProgress) { + myIsBrightnessAdjustmentInProgress = false; + } else if (isFlickScrollingEnabled()) { + myReader.getViewWidget().startAnimatedScrolling( + x, y, myReader.PageTurningOptions.AnimationSpeed.getValue() + ); + } + } + + @Override + public boolean onFingerLongPress(int x, int y) { + myReader.runAction(ActionCode.HIDE_TOAST); + + final ZLTextRegion region = findRegion(x, y, maxSelectionDistance(), ZLTextRegion.AnyRegionFilter); + if (region != null) { + final ZLTextRegion.Soul soul = region.getSoul(); + boolean doSelectRegion = false; + if (soul instanceof ZLTextWordRegionSoul) { + switch (myReader.MiscOptions.WordTappingAction.getValue()) { + case startSelecting: + myReader.runAction(ActionCode.SELECTION_HIDE_PANEL); + initSelection(x, y); + final SelectionCursor.Which cursor = findSelectionCursor(x, y); + if (cursor != null) { + moveSelectionCursorTo(cursor, x, y); + } + return true; + case selectSingleWord: + case openDictionary: + doSelectRegion = true; + break; + } + } else if (soul instanceof ZLTextImageRegionSoul) { + doSelectRegion = + myReader.ImageOptions.TapAction.getValue() != + ImageOptions.TapActionEnum.doNothing; + } else if (soul instanceof ZLTextHyperlinkRegionSoul) { + doSelectRegion = true; + } + + if (doSelectRegion) { + outlineRegion(region); + myReader.getViewWidget().reset(); + myReader.getViewWidget().repaint(); + return true; + } + } + return false; + } + + @Override + public void onFingerMoveAfterLongPress(int x, int y) { + final SelectionCursor.Which cursor = getSelectionCursorInMovement(); + if (cursor != null) { + moveSelectionCursorTo(cursor, x, y); + return; + } + + ZLTextRegion region = getOutlinedRegion(); + if (region != null) { + ZLTextRegion.Soul soul = region.getSoul(); + if (soul instanceof ZLTextHyperlinkRegionSoul || + soul instanceof ZLTextWordRegionSoul) { + if (myReader.MiscOptions.WordTappingAction.getValue() != + MiscOptions.WordTappingActionEnum.doNothing) { + region = findRegion(x, y, maxSelectionDistance(), ZLTextRegion.AnyRegionFilter); + if (region != null) { + soul = region.getSoul(); + if (soul instanceof ZLTextHyperlinkRegionSoul + || soul instanceof ZLTextWordRegionSoul) { + outlineRegion(region); + myReader.getViewWidget().reset(); + myReader.getViewWidget().repaint(); + } + } + } + } + } + } + + @Override + public void onFingerReleaseAfterLongPress(int x, int y) { + final SelectionCursor.Which cursor = getSelectionCursorInMovement(); + if (cursor != null) { + releaseSelectionCursor(); + return; + } + + final ZLTextRegion region = getOutlinedRegion(); + if (region != null) { + final ZLTextRegion.Soul soul = region.getSoul(); + + boolean doRunAction = false; + if (soul instanceof ZLTextWordRegionSoul) { + doRunAction = + myReader.MiscOptions.WordTappingAction.getValue() == + MiscOptions.WordTappingActionEnum.openDictionary; + } else if (soul instanceof ZLTextImageRegionSoul) { + doRunAction = + myReader.ImageOptions.TapAction.getValue() == + ImageOptions.TapActionEnum.openImageView; + } + + if (doRunAction) { + myReader.runAction(ActionCode.PROCESS_HYPERLINK); + } + } + } + + @Override + public void onFingerEventCancelled() { + final SelectionCursor.Which cursor = getSelectionCursorInMovement(); + if (cursor != null) { + releaseSelectionCursor(); + } + } + + public boolean onTrackballRotated(int diffX, int diffY) { + if (diffX == 0 && diffY == 0) { + return true; + } + + final Direction direction = (diffY != 0) ? + (diffY > 0 ? Direction.down : Direction.up) : + (diffX > 0 ? Direction.leftToRight : Direction.rightToLeft); + + new MoveCursorAction(myReader, direction).run(); + return true; + } + + @Override + public ZLTextStyleCollection getTextStyleCollection() { + return myViewOptions.getTextStyleCollection(); + } + + @Override + public ImageFitting getImageFitting() { + return myReader.ImageOptions.FitToScreen.getValue(); + } + + @Override + public int getLeftMargin() { + return myViewOptions.LeftMargin.getValue(); + } + + @Override + public int getRightMargin() { + return myViewOptions.RightMargin.getValue(); + } + + @Override + public int getTopMargin() { + return myViewOptions.TopMargin.getValue(); + } + + @Override + public int getBottomMargin() { + return myViewOptions.BottomMargin.getValue(); + } + + @Override + public int getSpaceBetweenColumns() { + return myViewOptions.SpaceBetweenColumns.getValue(); + } + + @Override + public boolean twoColumnView() { + return getContextHeight() <= getContextWidth() && myViewOptions.TwoColumnView.getValue(); + } + + @Override + public ZLFile getWallpaperFile() { + final String filePath = myViewOptions.getColorProfile().WallpaperOption.getValue(); + if ("".equals(filePath)) { + return null; + } + + final ZLFile file = ZLFile.createFileByPath(filePath); + if (file == null || !file.exists()) { + return null; + } + return file; + } + + @Override + public ZLPaintContext.FillMode getFillMode() { + return getWallpaperFile() instanceof ZLResourceFile + ? ZLPaintContext.FillMode.tileMirror + : myViewOptions.getColorProfile().FillModeOption.getValue(); + } + + @Override + public ZLColor getBackgroundColor() { + return myViewOptions.getColorProfile().BackgroundOption.getValue(); + } + + @Override + public ZLColor getSelectionBackgroundColor() { + return myViewOptions.getColorProfile().SelectionBackgroundOption.getValue(); + } + + @Override + public ZLColor getSelectionForegroundColor() { + return myViewOptions.getColorProfile().SelectionForegroundOption.getValue(); + } + + @Override + public ZLColor getTextColor(ZLTextHyperlink hyperlink) { + final ColorProfile profile = myViewOptions.getColorProfile(); + switch (hyperlink.Type) { + default: + case FBHyperlinkType.NONE: + return profile.RegularTextOption.getValue(); + case FBHyperlinkType.INTERNAL: + case FBHyperlinkType.FOOTNOTE: + return myReader.Collection.isHyperlinkVisited(myReader.getCurrentBook(), hyperlink.Id) + ? profile.VisitedHyperlinkTextOption.getValue() + : profile.HyperlinkTextOption.getValue(); + case FBHyperlinkType.EXTERNAL: + return profile.HyperlinkTextOption.getValue(); + } + } + + @Override + public ZLColor getHighlightingBackgroundColor() { + return myViewOptions.getColorProfile().HighlightingBackgroundOption.getValue(); + } + + @Override + public ZLColor getHighlightingForegroundColor() { + return myViewOptions.getColorProfile().HighlightingForegroundOption.getValue(); + } + + private abstract class Footer implements FooterArea { + private Runnable UpdateTask = new Runnable() { + public void run() { + myReader.getViewWidget().repaint(); + } + }; + + protected ArrayList myTOCMarks; + private int myMaxTOCMarksNumber = -1; + + public int getHeight() { + return myViewOptions.FooterHeight.getValue(); + } + + public synchronized void resetTOCMarks() { + myTOCMarks = null; + } + + protected synchronized void updateTOCMarks(BookModel model, int maxNumber) { + if (myTOCMarks != null && myMaxTOCMarksNumber == maxNumber) { + return; + } + + myTOCMarks = new ArrayList(); + myMaxTOCMarksNumber = maxNumber; + + TOCTree toc = model.TOCTree; + if (toc == null) { + return; + } + int maxLevel = Integer.MAX_VALUE; + if (toc.getSize() >= maxNumber) { + final int[] sizes = new int[10]; + for (TOCTree tocItem : toc) { + if (tocItem.Level < 10) { + ++sizes[tocItem.Level]; + } + } + for (int i = 1; i < sizes.length; ++i) { + sizes[i] += sizes[i - 1]; + } + for (maxLevel = sizes.length - 1; maxLevel >= 0; --maxLevel) { + if (sizes[maxLevel] < maxNumber) { + break; + } + } + } + for (TOCTree tocItem : toc.allSubtrees(maxLevel)) { + myTOCMarks.add(tocItem); + } + } + + protected String buildInfoString(PagePosition pagePosition, String separator) { + final StringBuilder info = new StringBuilder(); + final FooterOptions footerOptions = myViewOptions.getFooterOptions(); + + if (footerOptions.showProgressAsPages()) { + maybeAddSeparator(info, separator); + info.append(pagePosition.Current); + info.append("/"); + info.append(pagePosition.Total); + } + if (footerOptions.showProgressAsPercentage() && pagePosition.Total != 0) { + maybeAddSeparator(info, separator); + info.append(String.valueOf(100 * pagePosition.Current / pagePosition.Total)); + info.append("%"); + } + + if (footerOptions.ShowClock.getValue()) { + maybeAddSeparator(info, separator); + info.append(ZLibrary.Instance().getCurrentTimeString()); + } + if (footerOptions.ShowBattery.getValue()) { + maybeAddSeparator(info, separator); + info.append(myReader.getBatteryLevel()); + info.append("%"); + } + return info.toString(); + } + + private void maybeAddSeparator(StringBuilder info, String separator) { + if (info.length() > 0) { + info.append(separator); + } + } + + private List myFontEntry; + private Map myHeightMap = new HashMap(); + private Map myCharHeightMap = new HashMap(); + + protected synchronized int setFont(ZLPaintContext context, int height, boolean bold) { + final String family = myViewOptions.getFooterOptions().Font.getValue(); + if (myFontEntry == null || !family.equals(myFontEntry.get(0).Family)) { + myFontEntry = Collections.singletonList(FontEntry.systemEntry(family)); + } + final String key = family + (bold ? "N" : "B") + height; + final Integer cached = myHeightMap.get(key); + if (cached != null) { + context.setFont(myFontEntry, cached, bold, false, false, false); + final Integer charHeight = myCharHeightMap.get(key); + return charHeight != null ? charHeight : height; + } else { + int h = height + 2; + int charHeight = height; + final int max = height < 9 ? height - 1 : height - 2; + for (; h > 5; --h) { + context.setFont(myFontEntry, h, bold, false, false, false); + charHeight = context.getCharHeight('H'); + if (charHeight <= max) { + break; + } + } + myHeightMap.put(key, h); + myCharHeightMap.put(key, charHeight); + return charHeight; + } + } + } + + private class FooterOldStyle extends Footer { + public synchronized void paint(ZLPaintContext context) { + final ZLFile wallpaper = getWallpaperFile(); + if (wallpaper != null) { + context.clear(wallpaper, getFillMode()); + } else { + context.clear(getBackgroundColor()); + } + + final BookModel model = myReader.Model; + if (model == null) { + return; + } + + //final ZLColor bgColor = getBackgroundColor(); + // TODO: separate color option for footer color + final ZLColor fgColor = getTextColor(ZLTextHyperlink.NO_LINK); + final ZLColor fillColor = myViewOptions.getColorProfile().FooterFillOption.getValue(); + + final int left = getLeftMargin(); + final int right = context.getWidth() - getRightMargin(); + final int height = getHeight(); + final int lineWidth = height <= 10 ? 1 : 2; + final int delta = height <= 10 ? 0 : 1; + setFont(context, height, height > 10); + + final PagePosition pagePosition = FBView.this.pagePosition(); + + // draw info text + final String infoString = buildInfoString(pagePosition, " "); + final int infoWidth = context.getStringWidth(infoString); + context.setTextColor(fgColor); + context.drawString(right - infoWidth, height - delta, infoString); + + // draw gauge + final int gaugeRight = right - (infoWidth == 0 ? 0 : infoWidth + 10); + final int gaugeWidth = gaugeRight - left - 2 * lineWidth; + + context.setLineColor(fgColor); + context.setLineWidth(lineWidth); + context.drawLine(left, lineWidth, left, height - lineWidth); + context.drawLine(left, height - lineWidth, gaugeRight, height - lineWidth); + context.drawLine(gaugeRight, height - lineWidth, gaugeRight, lineWidth); + context.drawLine(gaugeRight, lineWidth, left, lineWidth); + + final int gaugeInternalRight = + left + lineWidth + (int) (1.0 * gaugeWidth * pagePosition.Current / pagePosition.Total); + + context.setFillColor(fillColor); + context.fillRectangle(left + 1, height - 2 * lineWidth, gaugeInternalRight, lineWidth + 1); + + final FooterOptions footerOptions = myViewOptions.getFooterOptions(); + if (footerOptions.ShowTOCMarks.getValue()) { + updateTOCMarks(model, footerOptions.MaxTOCMarks.getValue()); + final int fullLength = sizeOfFullText(); + for (TOCTree tocItem : myTOCMarks) { + TOCTree.Reference reference = tocItem.getReference(); + if (reference != null) { + final int refCoord = sizeOfTextBeforeParagraph(reference.ParagraphIndex); + final int xCoord = + left + 2 * lineWidth + (int) (1.0 * gaugeWidth * refCoord / fullLength); + context.drawLine(xCoord, height - lineWidth, xCoord, lineWidth); + } + } + } + } + } + + private class FooterNewStyle extends Footer { + public synchronized void paint(ZLPaintContext context) { + final ColorProfile cProfile = myViewOptions.getColorProfile(); + context.clear(cProfile.FooterNGBackgroundOption.getValue()); + + final BookModel model = myReader.Model; + if (model == null) { + return; + } + + final ZLColor textColor = cProfile.FooterNGForegroundOption.getValue(); + final ZLColor readColor = cProfile.FooterNGForegroundOption.getValue(); + final ZLColor unreadColor = cProfile.FooterNGForegroundUnreadOption.getValue(); + + final int left = getLeftMargin(); + final int right = context.getWidth() - getRightMargin(); + final int height = getHeight(); + final int lineWidth = height <= 12 ? 1 : 2; + final int charHeight = setFont(context, height, height > 12); + + final PagePosition pagePosition = FBView.this.pagePosition(); + + // draw info text + final String infoString = buildInfoString(pagePosition, " "); + final int infoWidth = context.getStringWidth(infoString); + context.setTextColor(textColor); + context.drawString(right - infoWidth, (height + charHeight + 1) / 2, infoString); + + // draw gauge + final int gaugeRight = right - (infoWidth == 0 ? 0 : infoWidth + 10); + final int gaugeInternalRight = + left + (int) (1.0 * (gaugeRight - left) * pagePosition.Current / pagePosition.Total + 0.5); + final int v = height / 2; + + context.setLineWidth(lineWidth); + context.setLineColor(readColor); + context.drawLine(left, v, gaugeInternalRight, v); + if (gaugeInternalRight < gaugeRight) { + context.setLineColor(unreadColor); + context.drawLine(gaugeInternalRight + 1, v, gaugeRight, v); + } + + // draw labels + final FooterOptions footerOptions = myViewOptions.getFooterOptions(); + if (footerOptions.ShowTOCMarks.getValue()) { + final TreeSet labels = new TreeSet(); + labels.add(left); + labels.add(gaugeRight); + updateTOCMarks(model, footerOptions.MaxTOCMarks.getValue()); + final int fullLength = sizeOfFullText(); + for (TOCTree tocItem : myTOCMarks) { + TOCTree.Reference reference = tocItem.getReference(); + if (reference != null) { + final int refCoord = sizeOfTextBeforeParagraph(reference.ParagraphIndex); + labels.add(left + (int) (1.0 * (gaugeRight - left) * refCoord / fullLength + 0.5)); + } + } + for (int l : labels) { + context.setLineColor(l <= gaugeInternalRight ? readColor : unreadColor); + context.drawLine(l, v + 3, l, v - lineWidth - 2); + } + } + } + } + + private Footer myFooter; + + @Override + public Footer getFooterArea() { + switch (myViewOptions.ScrollbarType.getValue()) { + case SCROLLBAR_SHOW_AS_FOOTER: + if (!(myFooter instanceof FooterNewStyle)) { + if (myFooter != null) { + myReader.removeTimerTask(myFooter.UpdateTask); + } + myFooter = new FooterNewStyle(); + myReader.addTimerTask(myFooter.UpdateTask, 15000); + } + break; + case SCROLLBAR_SHOW_AS_FOOTER_OLD_STYLE: + if (!(myFooter instanceof FooterOldStyle)) { + if (myFooter != null) { + myReader.removeTimerTask(myFooter.UpdateTask); + } + myFooter = new FooterOldStyle(); + myReader.addTimerTask(myFooter.UpdateTask, 15000); + } + break; + default: + if (myFooter != null) { + myReader.removeTimerTask(myFooter.UpdateTask); + myFooter = null; + } + break; + } + return myFooter; + } + + @Override + protected void releaseSelectionCursor() { + super.releaseSelectionCursor(); + if (getCountOfSelectedWords() > 0) { + myReader.runAction(ActionCode.SELECTION_SHOW_PANEL); + } + } + + public TextSnippet getSelectedSnippet() { + final ZLTextPosition start = getSelectionStartPosition(); + final ZLTextPosition end = getSelectionEndPosition(); + if (start == null || end == null) { + return null; + } + final TextBuildTraverser traverser = new TextBuildTraverser(this); + traverser.traverse(start, end); + return new FixedTextSnippet(start, end, traverser.getText()); + } + + public int getCountOfSelectedWords() { + final WordCountTraverser traverser = new WordCountTraverser(this); + if (!isSelectionEmpty()) { + traverser.traverse(getSelectionStartPosition(), getSelectionEndPosition()); + } + return traverser.getCount(); + } + + public static final int SCROLLBAR_SHOW_AS_FOOTER = 3; + public static final int SCROLLBAR_SHOW_AS_FOOTER_OLD_STYLE = 4; + + @Override + public int scrollbarType() { + return myViewOptions.ScrollbarType.getValue(); + } + + @Override + public Animation getAnimationType() { + return myReader.PageTurningOptions.Animation.getValue(); + } + + @Override + protected ZLPaintContext.ColorAdjustingMode getAdjustingModeForImages() { + if (myReader.ImageOptions.MatchBackground.getValue()) { + if (ColorProfile.DAY.equals(myViewOptions.getColorProfile().Name)) { + return ZLPaintContext.ColorAdjustingMode.DARKEN_TO_BACKGROUND; + } else { + return ZLPaintContext.ColorAdjustingMode.LIGHTEN_TO_BACKGROUND; + } + } else { + return ZLPaintContext.ColorAdjustingMode.NONE; + } + } + + @Override + public synchronized void onScrollingFinished(PageIndex pageIndex) { + super.onScrollingFinished(pageIndex); + myReader.storePosition(); + } + + @Override + protected ExtensionElementManager getExtensionManager() { + return myBookElementManager; + } } diff --git a/fBReader/src/main/java/org/geometerplus/zlibrary/core/view/ZLView.java b/fBReader/src/main/java/org/geometerplus/zlibrary/core/view/ZLView.java index aea6bec..9870a7c 100644 --- a/fBReader/src/main/java/org/geometerplus/zlibrary/core/view/ZLView.java +++ b/fBReader/src/main/java/org/geometerplus/zlibrary/core/view/ZLView.java @@ -61,6 +61,7 @@ abstract public interface FooterArea { public abstract void onFingerPress(int x, int y); public abstract void onFingerRelease(int x, int y); public abstract void onFingerMove(int x, int y); + public abstract void onFingerUp(int x, int y); public abstract boolean onFingerLongPress(int x, int y); public abstract void onFingerReleaseAfterLongPress(int x, int y); public abstract void onFingerMoveAfterLongPress(int x, int y); diff --git a/fBReader/src/main/java/org/geometerplus/zlibrary/core/view/ZLViewEnums.java b/fBReader/src/main/java/org/geometerplus/zlibrary/core/view/ZLViewEnums.java index 9e1e1bd..e8ea440 100644 --- a/fBReader/src/main/java/org/geometerplus/zlibrary/core/view/ZLViewEnums.java +++ b/fBReader/src/main/java/org/geometerplus/zlibrary/core/view/ZLViewEnums.java @@ -20,43 +20,43 @@ package org.geometerplus.zlibrary.core.view; public interface ZLViewEnums { - public enum PageIndex { - previous, current, next; - - public PageIndex getNext() { - switch (this) { - case previous: - return current; - case current: - return next; - default: - return null; - } - } - - public PageIndex getPrevious() { - switch (this) { - case next: - return current; - case current: - return previous; - default: - return null; - } - } - } - - public enum Direction { - leftToRight(true), rightToLeft(true), up(false), down(false); - - public final boolean IsHorizontal; - - Direction(boolean isHorizontal) { - IsHorizontal = isHorizontal; - } - } - - public enum Animation { - none, curl, slide, slideOldStyle, shift - } + public enum PageIndex { + previous, current, next; + + public PageIndex getNext() { + switch (this) { + case previous: + return current; + case current: + return next; + default: + return null; + } + } + + public PageIndex getPrevious() { + switch (this) { + case next: + return current; + case current: + return previous; + default: + return null; + } + } + } + + public enum Direction { + leftToRight(true), rightToLeft(true), up(false), down(false); + + public final boolean IsHorizontal; + + Direction(boolean isHorizontal) { + IsHorizontal = isHorizontal; + } + } + + public enum Animation { + none, curl, slide, slideOldStyle, shift + } } diff --git a/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/library/ZLAndroidLibrary.java b/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/library/ZLAndroidLibrary.java index 3976f8c..f5619ae 100644 --- a/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/library/ZLAndroidLibrary.java +++ b/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/library/ZLAndroidLibrary.java @@ -19,13 +19,8 @@ package org.geometerplus.zlibrary.ui.android.library; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; - import android.app.Application; import android.content.Context; -import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; @@ -34,12 +29,21 @@ import android.text.format.DateFormat; import android.util.DisplayMetrics; +import org.geometerplus.android.util.DeviceType; import org.geometerplus.zlibrary.core.filesystem.ZLFile; import org.geometerplus.zlibrary.core.filesystem.ZLResourceFile; import org.geometerplus.zlibrary.core.library.ZLibrary; -import org.geometerplus.zlibrary.core.options.*; +import org.geometerplus.zlibrary.core.options.ZLBooleanOption; +import org.geometerplus.zlibrary.core.options.ZLIntegerRangeOption; -import org.geometerplus.android.util.DeviceType; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.TreeSet; public final class ZLAndroidLibrary extends ZLibrary { public final ZLBooleanOption ShowStatusBarOption = new ZLBooleanOption("LookNFeel", "ShowStatusBar", false); @@ -56,7 +60,7 @@ public final class ZLAndroidLibrary extends ZLibrary { } public final ZLIntegerRangeOption BatteryLevelToTurnScreenOffOption = new ZLIntegerRangeOption("LookNFeel", "BatteryLevelToTurnScreenOff", 0, 100, 50); public final ZLBooleanOption DontTurnScreenOffDuringChargingOption = new ZLBooleanOption("LookNFeel", "DontTurnScreenOffDuringCharging", true); - public final ZLIntegerRangeOption ScreenBrightnessLevelOption = new ZLIntegerRangeOption("LookNFeel", "ScreenBrightnessLevel", 0, 100, 0); + public final ZLIntegerRangeOption ScreenBrightnessLevelOption = new ZLIntegerRangeOption("LookNFeel", "ScreenBrightnessLevel", 0, 100, 80); private final Application myApplication; diff --git a/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/view/ZLAndroidWidget.java b/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/view/ZLAndroidWidget.java index 47b02ef..ed21746 100644 --- a/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/view/ZLAndroidWidget.java +++ b/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/view/ZLAndroidWidget.java @@ -19,551 +19,596 @@ package org.geometerplus.zlibrary.ui.android.view; -import java.util.concurrent.Executors; -import java.util.concurrent.ExecutorService; - import android.content.Context; -import android.graphics.*; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; import android.util.AttributeSet; -import android.view.*; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import org.geometerplus.android.fbreader.FBReader; +import org.geometerplus.fbreader.Paths; +import org.geometerplus.fbreader.fbreader.FBReaderApp; import org.geometerplus.zlibrary.core.application.ZLApplication; import org.geometerplus.zlibrary.core.application.ZLKeyBindings; import org.geometerplus.zlibrary.core.util.SystemInfo; import org.geometerplus.zlibrary.core.view.ZLView; import org.geometerplus.zlibrary.core.view.ZLViewWidget; +import org.geometerplus.zlibrary.ui.android.view.animation.AnimationProvider; +import org.geometerplus.zlibrary.ui.android.view.animation.CurlAnimationProvider; +import org.geometerplus.zlibrary.ui.android.view.animation.NoneAnimationProvider; +import org.geometerplus.zlibrary.ui.android.view.animation.ShiftAnimationProvider; +import org.geometerplus.zlibrary.ui.android.view.animation.SlideAnimationProvider; +import org.geometerplus.zlibrary.ui.android.view.animation.SlideOldStyleAnimationProvider; -import org.geometerplus.zlibrary.ui.android.view.animation.*; - -import org.geometerplus.fbreader.Paths; -import org.geometerplus.android.fbreader.FBReader; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public class ZLAndroidWidget extends MainView implements ZLViewWidget, View.OnLongClickListener { - public final ExecutorService PrepareService = Executors.newSingleThreadExecutor(); - - private final Paint myPaint = new Paint(); - - private final BitmapManagerImpl myBitmapManager = new BitmapManagerImpl(this); - private Bitmap myFooterBitmap; - private final SystemInfo mySystemInfo; - - public ZLAndroidWidget(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - mySystemInfo = Paths.systemInfo(context); - init(); - } - - public ZLAndroidWidget(Context context, AttributeSet attrs) { - super(context, attrs); - mySystemInfo = Paths.systemInfo(context); - init(); - } - - public ZLAndroidWidget(Context context) { - super(context); - mySystemInfo = Paths.systemInfo(context); - init(); - } - - private void init() { - // next line prevent ignoring first onKeyDown DPad event - // after any dialog was closed - setFocusableInTouchMode(true); - setDrawingCacheEnabled(false); - setOnLongClickListener(this); - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - getAnimationProvider().terminate(); - if (myScreenIsTouched) { - final ZLView view = ZLApplication.Instance().getCurrentView(); - myScreenIsTouched = false; - view.onScrollingFinished(ZLView.PageIndex.current); - } - } - - @Override - protected void onDraw(final Canvas canvas) { - final Context context = getContext(); - if (context instanceof FBReader) { - ((FBReader)context).createWakeLock(); - } else { - System.err.println("A surprise: view's context is not an FBReader"); - } - super.onDraw(canvas); + public final ExecutorService PrepareService = Executors.newSingleThreadExecutor(); + + private final Paint myPaint = new Paint(); + + private final BitmapManagerImpl myBitmapManager = new BitmapManagerImpl(this); + private Bitmap myFooterBitmap; + private final SystemInfo mySystemInfo; + + public ZLAndroidWidget(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mySystemInfo = Paths.systemInfo(context); + init(); + } + + public ZLAndroidWidget(Context context, AttributeSet attrs) { + super(context, attrs); + mySystemInfo = Paths.systemInfo(context); + init(); + } + + public ZLAndroidWidget(Context context) { + super(context); + mySystemInfo = Paths.systemInfo(context); + init(); + } + + private void init() { + // next line prevent ignoring first onKeyDown DPad event + // after any dialog was closed + setFocusableInTouchMode(true); + setDrawingCacheEnabled(false); + setOnLongClickListener(this); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + getAnimationProvider().terminate(); + if (myScreenIsTouched) { + final ZLView view = ZLApplication.Instance().getCurrentView(); + myScreenIsTouched = false; + view.onScrollingFinished(ZLView.PageIndex.current); + } + } + + @Override + protected void onDraw(final Canvas canvas) { + final Context context = getContext(); + if (context instanceof FBReader) { + ((FBReader) context).createWakeLock(); + } else { + System.err.println("A surprise: view's context is not an FBReader"); + } + super.onDraw(canvas); // final int w = getWidth(); // final int h = getMainAreaHeight(); - myBitmapManager.setSize(getWidth(), getMainAreaHeight()); - if (getAnimationProvider().inProgress()) { - onDrawInScrolling(canvas); - } else { - onDrawStatic(canvas); - ZLApplication.Instance().onRepaintFinished(); - } - } - - private AnimationProvider myAnimationProvider; - private ZLView.Animation myAnimationType; - private AnimationProvider getAnimationProvider() { - final ZLView.Animation type = ZLApplication.Instance().getCurrentView().getAnimationType(); - if (myAnimationProvider == null || myAnimationType != type) { - myAnimationType = type; - switch (type) { - case none: - myAnimationProvider = new NoneAnimationProvider(myBitmapManager); - break; - case curl: - myAnimationProvider = new CurlAnimationProvider(myBitmapManager); - break; - case slide: - myAnimationProvider = new SlideAnimationProvider(myBitmapManager); - break; - case slideOldStyle: - myAnimationProvider = new SlideOldStyleAnimationProvider(myBitmapManager); - break; - case shift: - myAnimationProvider = new ShiftAnimationProvider(myBitmapManager); - break; - } - } - return myAnimationProvider; - } - - private void onDrawInScrolling(Canvas canvas) { - final ZLView view = ZLApplication.Instance().getCurrentView(); - - final AnimationProvider animator = getAnimationProvider(); - final AnimationProvider.Mode oldMode = animator.getMode(); - animator.doStep(); - if (animator.inProgress()) { - animator.draw(canvas); - if (animator.getMode().Auto) { - postInvalidate(); - } - drawFooter(canvas, animator); - } else { - switch (oldMode) { - case AnimatedScrollingForward: - { - final ZLView.PageIndex index = animator.getPageToScrollTo(); - myBitmapManager.shift(index == ZLView.PageIndex.next); - view.onScrollingFinished(index); - ZLApplication.Instance().onRepaintFinished(); - break; - } - case AnimatedScrollingBackward: - view.onScrollingFinished(ZLView.PageIndex.current); - break; - } - onDrawStatic(canvas); - } - } - - @Override - public void reset() { - myBitmapManager.reset(); - } - - @Override - public void repaint() { - postInvalidate(); - } - - @Override - public void startManualScrolling(int x, int y, ZLView.Direction direction) { - final AnimationProvider animator = getAnimationProvider(); - animator.setup(direction, getWidth(), getMainAreaHeight(), myColorLevel); - animator.startManualScrolling(x, y); - } - - @Override - public void scrollManuallyTo(int x, int y) { - final ZLView view = ZLApplication.Instance().getCurrentView(); - final AnimationProvider animator = getAnimationProvider(); - if (view.canScroll(animator.getPageToScrollTo(x, y))) { - animator.scrollTo(x, y); - postInvalidate(); - } - } - - @Override - public void startAnimatedScrolling(ZLView.PageIndex pageIndex, int x, int y, ZLView.Direction direction, int speed) { - final ZLView view = ZLApplication.Instance().getCurrentView(); - if (pageIndex == ZLView.PageIndex.current || !view.canScroll(pageIndex)) { - return; - } - final AnimationProvider animator = getAnimationProvider(); - animator.setup(direction, getWidth(), getMainAreaHeight(), myColorLevel); - animator.startAnimatedScrolling(pageIndex, x, y, speed); - if (animator.getMode().Auto) { - postInvalidate(); - } - } - - @Override - public void startAnimatedScrolling(ZLView.PageIndex pageIndex, ZLView.Direction direction, int speed) { - final ZLView view = ZLApplication.Instance().getCurrentView(); - if (pageIndex == ZLView.PageIndex.current || !view.canScroll(pageIndex)) { - return; - } - final AnimationProvider animator = getAnimationProvider(); - animator.setup(direction, getWidth(), getMainAreaHeight(), myColorLevel); - animator.startAnimatedScrolling(pageIndex, null, null, speed); - if (animator.getMode().Auto) { - postInvalidate(); - } - } - - @Override - public void startAnimatedScrolling(int x, int y, int speed) { - final ZLView view = ZLApplication.Instance().getCurrentView(); - final AnimationProvider animator = getAnimationProvider(); - if (!view.canScroll(animator.getPageToScrollTo(x, y))) { - animator.terminate(); - return; - } - animator.startAnimatedScrolling(x, y, speed); - postInvalidate(); - } - - void drawOnBitmap(Bitmap bitmap, ZLView.PageIndex index) { - final ZLView view = ZLApplication.Instance().getCurrentView(); - if (view == null) { - return; - } - - final ZLAndroidPaintContext context = new ZLAndroidPaintContext( - mySystemInfo, - new Canvas(bitmap), - new ZLAndroidPaintContext.Geometry( - getWidth(), - getHeight(), - getWidth(), - getMainAreaHeight(), - 0, - 0 - ), - view.isScrollbarShown() ? getVerticalScrollbarWidth() : 0 - ); - view.paint(context, index); - } - - private void drawFooter(Canvas canvas, AnimationProvider animator) { - final ZLView view = ZLApplication.Instance().getCurrentView(); - final ZLView.FooterArea footer = view.getFooterArea(); - - if (footer == null) { - myFooterBitmap = null; - return; - } - - if (myFooterBitmap != null && - (myFooterBitmap.getWidth() != getWidth() || - myFooterBitmap.getHeight() != footer.getHeight())) { - myFooterBitmap = null; - } - if (myFooterBitmap == null) { - myFooterBitmap = Bitmap.createBitmap(getWidth(), footer.getHeight(), Bitmap.Config.RGB_565); - } - final ZLAndroidPaintContext context = new ZLAndroidPaintContext( - mySystemInfo, - new Canvas(myFooterBitmap), - new ZLAndroidPaintContext.Geometry( - getWidth(), - getHeight(), - getWidth(), - footer.getHeight(), - 0, - getMainAreaHeight() - ), - view.isScrollbarShown() ? getVerticalScrollbarWidth() : 0 - ); - footer.paint(context); - final int voffset = getHeight() - footer.getHeight(); - if (animator != null) { - animator.drawFooterBitmap(canvas, myFooterBitmap, voffset); - } else { - canvas.drawBitmap(myFooterBitmap, 0, voffset, myPaint); - } - } - - private void onDrawStatic(final Canvas canvas) { - canvas.drawBitmap(myBitmapManager.getBitmap(ZLView.PageIndex.current), 0, 0, myPaint); - drawFooter(canvas, null); - post(new Runnable() { - public void run() { - PrepareService.execute(new Runnable() { - public void run() { - final ZLView view = ZLApplication.Instance().getCurrentView(); - final ZLAndroidPaintContext context = new ZLAndroidPaintContext( - mySystemInfo, - canvas, - new ZLAndroidPaintContext.Geometry( - getWidth(), - getHeight(), - getWidth(), - getMainAreaHeight(), - 0, - 0 - ), - view.isScrollbarShown() ? getVerticalScrollbarWidth() : 0 - ); - view.preparePage(context, ZLView.PageIndex.next); - } - }); - } - }); - } - - @Override - public boolean onTrackballEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - onKeyDown(KeyEvent.KEYCODE_DPAD_CENTER, null); - } else { - ZLApplication.Instance().getCurrentView().onTrackballRotated((int)(10 * event.getX()), (int)(10 * event.getY())); - } - return true; - } - - private class LongClickRunnable implements Runnable { - @Override - public void run() { - if (performLongClick()) { - myLongClickPerformed = true; - } - } - } - private volatile LongClickRunnable myPendingLongClickRunnable; - private volatile boolean myLongClickPerformed; - - private void postLongClickRunnable() { - myLongClickPerformed = false; - myPendingPress = false; - if (myPendingLongClickRunnable == null) { - myPendingLongClickRunnable = new LongClickRunnable(); - } - postDelayed(myPendingLongClickRunnable, 2 * ViewConfiguration.getLongPressTimeout()); - } - - private class ShortClickRunnable implements Runnable { - @Override - public void run() { - final ZLView view = ZLApplication.Instance().getCurrentView(); - view.onFingerSingleTap(myPressedX, myPressedY); - myPendingPress = false; - myPendingShortClickRunnable = null; - } - } - private volatile ShortClickRunnable myPendingShortClickRunnable; - - private volatile boolean myPendingPress; - private volatile boolean myPendingDoubleTap; - private int myPressedX, myPressedY; - private boolean myScreenIsTouched; - @Override - public boolean onTouchEvent(MotionEvent event) { - int x = (int)event.getX(); - int y = (int)event.getY(); - - final ZLView view = ZLApplication.Instance().getCurrentView(); - switch (event.getAction()) { - case MotionEvent.ACTION_CANCEL: - myPendingDoubleTap = false; - myPendingPress = false; - myScreenIsTouched = false; - myLongClickPerformed = false; - if (myPendingShortClickRunnable != null) { - removeCallbacks(myPendingShortClickRunnable); - myPendingShortClickRunnable = null; - } - if (myPendingLongClickRunnable != null) { - removeCallbacks(myPendingLongClickRunnable); - myPendingLongClickRunnable = null; - } - view.onFingerEventCancelled(); - break; - case MotionEvent.ACTION_UP: - if (myPendingDoubleTap) { - view.onFingerDoubleTap(x, y); - } else if (myLongClickPerformed) { - view.onFingerReleaseAfterLongPress(x, y); - } else { - if (myPendingLongClickRunnable != null) { - removeCallbacks(myPendingLongClickRunnable); - myPendingLongClickRunnable = null; - } - if (myPendingPress) { - if (view.isDoubleTapSupported()) { - if (myPendingShortClickRunnable == null) { - myPendingShortClickRunnable = new ShortClickRunnable(); - } - postDelayed(myPendingShortClickRunnable, ViewConfiguration.getDoubleTapTimeout()); - } else { - view.onFingerSingleTap(x, y); - } - } else { - view.onFingerRelease(x, y); - } - } - myPendingDoubleTap = false; - myPendingPress = false; - myScreenIsTouched = false; - break; - case MotionEvent.ACTION_DOWN: - if (myPendingShortClickRunnable != null) { - removeCallbacks(myPendingShortClickRunnable); - myPendingShortClickRunnable = null; - myPendingDoubleTap = true; - } else { - postLongClickRunnable(); - myPendingPress = true; - } - myScreenIsTouched = true; - myPressedX = x; - myPressedY = y; - break; - case MotionEvent.ACTION_MOVE: - { - final int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); - final boolean isAMove = - Math.abs(myPressedX - x) > slop || Math.abs(myPressedY - y) > slop; - if (isAMove) { - myPendingDoubleTap = false; - } - if (myLongClickPerformed) { - view.onFingerMoveAfterLongPress(x, y); - } else { - if (myPendingPress) { - if (isAMove) { - if (myPendingShortClickRunnable != null) { - removeCallbacks(myPendingShortClickRunnable); - myPendingShortClickRunnable = null; - } - if (myPendingLongClickRunnable != null) { - removeCallbacks(myPendingLongClickRunnable); - } - view.onFingerPress(myPressedX, myPressedY); - myPendingPress = false; - } - } - if (!myPendingPress) { - view.onFingerMove(x, y); - } - } - break; - } - } - - return true; - } - - @Override - public boolean onLongClick(View v) { - final ZLView view = ZLApplication.Instance().getCurrentView(); - return view.onFingerLongPress(myPressedX, myPressedY); - } - - private int myKeyUnderTracking = -1; - private long myTrackingStartTime; - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - final ZLApplication application = ZLApplication.Instance(); - final ZLKeyBindings bindings = application.keyBindings(); - - if (bindings.hasBinding(keyCode, true) || - bindings.hasBinding(keyCode, false)) { - if (myKeyUnderTracking != -1) { - if (myKeyUnderTracking == keyCode) { - return true; - } else { - myKeyUnderTracking = -1; - } - } - if (bindings.hasBinding(keyCode, true)) { - myKeyUnderTracking = keyCode; - myTrackingStartTime = System.currentTimeMillis(); - return true; - } else { - return application.runActionByKey(keyCode, false); - } - } else { - return false; - } - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (myKeyUnderTracking != -1) { - if (myKeyUnderTracking == keyCode) { - final boolean longPress = System.currentTimeMillis() > - myTrackingStartTime + ViewConfiguration.getLongPressTimeout(); - ZLApplication.Instance().runActionByKey(keyCode, longPress); - } - myKeyUnderTracking = -1; - return true; - } else { - final ZLKeyBindings bindings = ZLApplication.Instance().keyBindings(); - return - bindings.hasBinding(keyCode, false) || - bindings.hasBinding(keyCode, true); - } - } - - @Override - protected int computeVerticalScrollExtent() { - final ZLView view = ZLApplication.Instance().getCurrentView(); - if (!view.isScrollbarShown()) { - return 0; - } - final AnimationProvider animator = getAnimationProvider(); - if (animator.inProgress()) { - final int from = view.getScrollbarThumbLength(ZLView.PageIndex.current); - final int to = view.getScrollbarThumbLength(animator.getPageToScrollTo()); - final int percent = animator.getScrolledPercent(); - return (from * (100 - percent) + to * percent) / 100; - } else { - return view.getScrollbarThumbLength(ZLView.PageIndex.current); - } - } - - @Override - protected int computeVerticalScrollOffset() { - final ZLView view = ZLApplication.Instance().getCurrentView(); - if (!view.isScrollbarShown()) { - return 0; - } - final AnimationProvider animator = getAnimationProvider(); - if (animator.inProgress()) { - final int from = view.getScrollbarThumbPosition(ZLView.PageIndex.current); - final int to = view.getScrollbarThumbPosition(animator.getPageToScrollTo()); - final int percent = animator.getScrolledPercent(); - return (from * (100 - percent) + to * percent) / 100; - } else { - return view.getScrollbarThumbPosition(ZLView.PageIndex.current); - } - } - - @Override - protected int computeVerticalScrollRange() { - final ZLView view = ZLApplication.Instance().getCurrentView(); - if (!view.isScrollbarShown()) { - return 0; - } - return view.getScrollbarFullSize(); - } - - private int getMainAreaHeight() { - final ZLView.FooterArea footer = ZLApplication.Instance().getCurrentView().getFooterArea(); - return footer != null ? getHeight() - footer.getHeight() : getHeight(); - } - - @Override - protected void updateColorLevel() { - ViewUtil.setColorLevel(myPaint, myColorLevel); - } + myBitmapManager.setSize(getWidth(), getMainAreaHeight()); + if (getAnimationProvider().inProgress()) { + onDrawInScrolling(canvas); + } else { + onDrawStatic(canvas); + ZLApplication.Instance().onRepaintFinished(); + } + } + + private AnimationProvider myAnimationProvider; + private ZLView.Animation myAnimationType; + + private AnimationProvider getAnimationProvider() { + final ZLView.Animation type = ZLApplication.Instance().getCurrentView().getAnimationType(); + if (myAnimationProvider == null || myAnimationType != type) { + myAnimationType = type; + switch (type) { + case none: + myAnimationProvider = new NoneAnimationProvider(myBitmapManager); + break; + case curl: + myAnimationProvider = new CurlAnimationProvider(myBitmapManager); + break; + case slide: + myAnimationProvider = new SlideAnimationProvider(myBitmapManager); + break; + case slideOldStyle: + myAnimationProvider = new SlideOldStyleAnimationProvider(myBitmapManager); + break; + case shift: + myAnimationProvider = new ShiftAnimationProvider(myBitmapManager); + break; + } + } + return myAnimationProvider; + } + + private void onDrawInScrolling(Canvas canvas) { + final ZLView view = ZLApplication.Instance().getCurrentView(); + + final AnimationProvider animator = getAnimationProvider(); + final AnimationProvider.Mode oldMode = animator.getMode(); + animator.doStep(); + if (animator.inProgress()) { + animator.draw(canvas); + if (animator.getMode().Auto) { + postInvalidate(); + } + drawFooter(canvas, animator); + } else { + switch (oldMode) { + case AnimatedScrollingForward: { + final ZLView.PageIndex index = animator.getPageToScrollTo(); + myBitmapManager.shift(index == ZLView.PageIndex.next); + view.onScrollingFinished(index); + ZLApplication.Instance().onRepaintFinished(); + break; + } + case AnimatedScrollingBackward: + view.onScrollingFinished(ZLView.PageIndex.current); + break; + } + onDrawStatic(canvas); + } + } + + @Override + public void reset() { + myBitmapManager.reset(); + } + + @Override + public void repaint() { + postInvalidate(); + } + + @Override + public void startManualScrolling(int x, int y, ZLView.Direction direction) { + final AnimationProvider animator = getAnimationProvider(); + animator.setup(direction, getWidth(), getMainAreaHeight(), myColorLevel); + animator.startManualScrolling(x, y); + } + + @Override + public void scrollManuallyTo(int x, int y) { + final ZLView view = ZLApplication.Instance().getCurrentView(); + final AnimationProvider animator = getAnimationProvider(); + if (view.canScroll(animator.getPageToScrollTo(x, y))) { + animator.scrollTo(x, y); + postInvalidate(); + } + } + + @Override + public void startAnimatedScrolling(ZLView.PageIndex pageIndex, int x, int y, ZLView.Direction direction, int speed) { + final ZLView view = ZLApplication.Instance().getCurrentView(); + if (pageIndex == ZLView.PageIndex.current || !view.canScroll(pageIndex)) { + return; + } + final AnimationProvider animator = getAnimationProvider(); + animator.setup(direction, getWidth(), getMainAreaHeight(), myColorLevel); + animator.startAnimatedScrolling(pageIndex, x, y, speed); + if (animator.getMode().Auto) { + postInvalidate(); + } + } + + @Override + public void startAnimatedScrolling(ZLView.PageIndex pageIndex, ZLView.Direction direction, int speed) { + final ZLView view = ZLApplication.Instance().getCurrentView(); + if (pageIndex == ZLView.PageIndex.current || !view.canScroll(pageIndex)) { + return; + } + final AnimationProvider animator = getAnimationProvider(); + animator.setup(direction, getWidth(), getMainAreaHeight(), myColorLevel); + animator.startAnimatedScrolling(pageIndex, null, null, speed); + if (animator.getMode().Auto) { + postInvalidate(); + } + } + + @Override + public void startAnimatedScrolling(int x, int y, int speed) { + final ZLView view = ZLApplication.Instance().getCurrentView(); + final AnimationProvider animator = getAnimationProvider(); + if (!view.canScroll(animator.getPageToScrollTo(x, y))) { + animator.terminate(); + return; + } + animator.startAnimatedScrolling(x, y, speed); + postInvalidate(); + } + + void drawOnBitmap(Bitmap bitmap, ZLView.PageIndex index) { + final ZLView view = ZLApplication.Instance().getCurrentView(); + if (view == null) { + return; + } + + final ZLAndroidPaintContext context = new ZLAndroidPaintContext( + mySystemInfo, + new Canvas(bitmap), + new ZLAndroidPaintContext.Geometry( + getWidth(), + getHeight(), + getWidth(), + getMainAreaHeight(), + 0, + 0 + ), + view.isScrollbarShown() ? getVerticalScrollbarWidth() : 0 + ); + view.paint(context, index); + } + + private void drawFooter(Canvas canvas, AnimationProvider animator) { + final ZLView view = ZLApplication.Instance().getCurrentView(); + final ZLView.FooterArea footer = view.getFooterArea(); + + if (footer == null) { + myFooterBitmap = null; + return; + } + + if (myFooterBitmap != null && + (myFooterBitmap.getWidth() != getWidth() || + myFooterBitmap.getHeight() != footer.getHeight())) { + myFooterBitmap = null; + } + if (myFooterBitmap == null) { + myFooterBitmap = Bitmap.createBitmap(getWidth(), footer.getHeight(), Bitmap.Config.RGB_565); + } + final ZLAndroidPaintContext context = new ZLAndroidPaintContext( + mySystemInfo, + new Canvas(myFooterBitmap), + new ZLAndroidPaintContext.Geometry( + getWidth(), + getHeight(), + getWidth(), + footer.getHeight(), + 0, + getMainAreaHeight() + ), + view.isScrollbarShown() ? getVerticalScrollbarWidth() : 0 + ); + footer.paint(context); + final int voffset = getHeight() - footer.getHeight(); + if (animator != null) { + animator.drawFooterBitmap(canvas, myFooterBitmap, voffset); + } else { + canvas.drawBitmap(myFooterBitmap, 0, voffset, myPaint); + } + } + + private void onDrawStatic(final Canvas canvas) { + canvas.drawBitmap(myBitmapManager.getBitmap(ZLView.PageIndex.current), 0, 0, myPaint); + drawFooter(canvas, null); + post(new Runnable() { + public void run() { + PrepareService.execute(new Runnable() { + public void run() { + final ZLView view = ZLApplication.Instance().getCurrentView(); + final ZLAndroidPaintContext context = new ZLAndroidPaintContext( + mySystemInfo, + canvas, + new ZLAndroidPaintContext.Geometry( + getWidth(), + getHeight(), + getWidth(), + getMainAreaHeight(), + 0, + 0 + ), + view.isScrollbarShown() ? getVerticalScrollbarWidth() : 0 + ); + view.preparePage(context, ZLView.PageIndex.next); + } + }); + } + }); + } + + @Override + public boolean onTrackballEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + onKeyDown(KeyEvent.KEYCODE_DPAD_CENTER, null); + } else { + ZLApplication.Instance().getCurrentView().onTrackballRotated((int) (10 * event.getX()), (int) (10 * event.getY())); + } + return true; + } + + private class LongClickRunnable implements Runnable { + @Override + public void run() { + if (performLongClick()) { + myLongClickPerformed = true; + } + } + } + + private volatile LongClickRunnable myPendingLongClickRunnable; + private volatile boolean myLongClickPerformed; + + private void postLongClickRunnable() { + myLongClickPerformed = false; + myPendingPress = false; + if (myPendingLongClickRunnable == null) { + myPendingLongClickRunnable = new LongClickRunnable(); + } + postDelayed(myPendingLongClickRunnable, 2 * ViewConfiguration.getLongPressTimeout()); + } + + private class ShortClickRunnable implements Runnable { + @Override + public void run() { + final ZLView view = ZLApplication.Instance().getCurrentView(); + view.onFingerSingleTap(myPressedX, myPressedY); + myPendingPress = false; + myPendingShortClickRunnable = null; + } + } + + private volatile ShortClickRunnable myPendingShortClickRunnable; + + private volatile boolean myPendingPress; + private volatile boolean myPendingDoubleTap; + private int myPressedX, myPressedY; + private boolean myScreenIsTouched; + private static final int isUpOrDownMove_vertical = 1; + private static final int isUpOrDownMove_horizontal = 2; + private static final int isAMove = 3; + private static final int NONE = 0; + private static int mMoveState = NONE; + + + @Override + public boolean onTouchEvent(MotionEvent event) { + int x = (int) event.getX(); + int y = (int) event.getY(); + + final ZLView view = ZLApplication.Instance().getCurrentView(); + switch (event.getAction()) { + case MotionEvent.ACTION_CANCEL: + myPendingDoubleTap = false; + myPendingPress = false; + myScreenIsTouched = false; + myLongClickPerformed = false; + if (myPendingShortClickRunnable != null) { + removeCallbacks(myPendingShortClickRunnable); + myPendingShortClickRunnable = null; + } + if (myPendingLongClickRunnable != null) { + removeCallbacks(myPendingLongClickRunnable); + myPendingLongClickRunnable = null; + } + view.onFingerEventCancelled(); + break; + case MotionEvent.ACTION_UP: + if (myPendingDoubleTap) { + view.onFingerDoubleTap(x, y); + } else if (myLongClickPerformed) { + view.onFingerReleaseAfterLongPress(x, y); + } else { + if (myPendingLongClickRunnable != null) { + removeCallbacks(myPendingLongClickRunnable); + myPendingLongClickRunnable = null; + } + if (myPendingPress) { + if (view.isDoubleTapSupported()) { + if (myPendingShortClickRunnable == null) { + myPendingShortClickRunnable = new ShortClickRunnable(); + } + postDelayed(myPendingShortClickRunnable, ViewConfiguration.getDoubleTapTimeout()); + } else { + if(mMoveState == isUpOrDownMove_horizontal || mMoveState == isUpOrDownMove_vertical){ + + }else{ + view.onFingerSingleTap(x, y); + } + } + } else { + view.onFingerRelease(x, y); + } + mMoveState = 0; + } + myPendingDoubleTap = false; + myPendingPress = false; + myScreenIsTouched = false; + break; + case MotionEvent.ACTION_DOWN: + if (myPendingShortClickRunnable != null) { + removeCallbacks(myPendingShortClickRunnable); + myPendingShortClickRunnable = null; + myPendingDoubleTap = true; + } else { + postLongClickRunnable(); + myPendingPress = true; + } + myScreenIsTouched = true; + myPressedX = x; + myPressedY = y; + break; + case MotionEvent.ACTION_MOVE: { + if (mMoveState == NONE) { + FBReaderApp fbReaderApp = (FBReaderApp) FBReaderApp.Instance(); + boolean isHorizotal = fbReaderApp.PageTurningOptions.Horizontal.getValue(); + + final int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + boolean isUpOrDownMove_horizontal_boolean = isHorizotal && Math.abs(myPressedY - y) > slop && Math.abs(myPressedX - x) < slop; + boolean isUpOrDownMove_vertical_boolean = !isHorizotal && Math.abs(myPressedY - y) < slop && Math.abs(myPressedX - x) > slop; + if (isUpOrDownMove_horizontal_boolean || isUpOrDownMove_vertical_boolean) { + mMoveState = isUpOrDownMove_horizontal; + } + + boolean isAMove_boolean = + (isHorizotal && Math.abs(myPressedX - x) > slop) || (!isHorizotal && Math.abs(myPressedY - y) > slop); + if (isAMove_boolean) { + mMoveState = isAMove; + } + } + if (mMoveState == isUpOrDownMove_horizontal || mMoveState == isUpOrDownMove_vertical) { + view.onFingerUp(x, y); + break; + } + if (mMoveState == isAMove) { +// if (isAMove == 3) { + myPendingDoubleTap = false; +// } + if (myLongClickPerformed) { + view.onFingerMoveAfterLongPress(x, y); + } else { + Log.d("test", "onTouchEvent: isMove " + isAMove + " is isUpOrDownMove_horizotal " + isUpOrDownMove_horizontal + " isUpOrDownMove_vertical " + isUpOrDownMove_vertical); + if (myPendingPress) { +// if (isAMove == 3) { + if (myPendingShortClickRunnable != null) { + removeCallbacks(myPendingShortClickRunnable); + myPendingShortClickRunnable = null; + } + if (myPendingLongClickRunnable != null) { + removeCallbacks(myPendingLongClickRunnable); + } + view.onFingerPress(myPressedX, myPressedY); + myPendingPress = false; +// } + } + if (!myPendingPress ) { + view.onFingerMove(x, y); + } + } + } + + break; + } + } + + return true; + } + + @Override + public boolean onLongClick(View v) { + final ZLView view = ZLApplication.Instance().getCurrentView(); + return view.onFingerLongPress(myPressedX, myPressedY); + } + + private int myKeyUnderTracking = -1; + private long myTrackingStartTime; + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + final ZLApplication application = ZLApplication.Instance(); + final ZLKeyBindings bindings = application.keyBindings(); + + if (bindings.hasBinding(keyCode, true) || + bindings.hasBinding(keyCode, false)) { + if (myKeyUnderTracking != -1) { + if (myKeyUnderTracking == keyCode) { + return true; + } else { + myKeyUnderTracking = -1; + } + } + if (bindings.hasBinding(keyCode, true)) { + myKeyUnderTracking = keyCode; + myTrackingStartTime = System.currentTimeMillis(); + return true; + } else { + return application.runActionByKey(keyCode, false); + } + } else { + return false; + } + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (myKeyUnderTracking != -1) { + if (myKeyUnderTracking == keyCode) { + final boolean longPress = System.currentTimeMillis() > + myTrackingStartTime + ViewConfiguration.getLongPressTimeout(); + ZLApplication.Instance().runActionByKey(keyCode, longPress); + } + myKeyUnderTracking = -1; + return true; + } else { + final ZLKeyBindings bindings = ZLApplication.Instance().keyBindings(); + return + bindings.hasBinding(keyCode, false) || + bindings.hasBinding(keyCode, true); + } + } + + @Override + protected int computeVerticalScrollExtent() { + final ZLView view = ZLApplication.Instance().getCurrentView(); + if (!view.isScrollbarShown()) { + return 0; + } + final AnimationProvider animator = getAnimationProvider(); + if (animator.inProgress()) { + final int from = view.getScrollbarThumbLength(ZLView.PageIndex.current); + final int to = view.getScrollbarThumbLength(animator.getPageToScrollTo()); + final int percent = animator.getScrolledPercent(); + return (from * (100 - percent) + to * percent) / 100; + } else { + return view.getScrollbarThumbLength(ZLView.PageIndex.current); + } + } + + @Override + protected int computeVerticalScrollOffset() { + final ZLView view = ZLApplication.Instance().getCurrentView(); + if (!view.isScrollbarShown()) { + return 0; + } + final AnimationProvider animator = getAnimationProvider(); + if (animator.inProgress()) { + final int from = view.getScrollbarThumbPosition(ZLView.PageIndex.current); + final int to = view.getScrollbarThumbPosition(animator.getPageToScrollTo()); + final int percent = animator.getScrolledPercent(); + return (from * (100 - percent) + to * percent) / 100; + } else { + return view.getScrollbarThumbPosition(ZLView.PageIndex.current); + } + } + + @Override + protected int computeVerticalScrollRange() { + final ZLView view = ZLApplication.Instance().getCurrentView(); + if (!view.isScrollbarShown()) { + return 0; + } + return view.getScrollbarFullSize(); + } + + private int getMainAreaHeight() { + final ZLView.FooterArea footer = ZLApplication.Instance().getCurrentView().getFooterArea(); + return footer != null ? getHeight() - footer.getHeight() : getHeight(); + } + + @Override + protected void updateColorLevel() { + ViewUtil.setColorLevel(myPaint, myColorLevel); + } } diff --git a/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/view/animation/AnimationProvider.java b/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/view/animation/AnimationProvider.java index 5f6c8ef..5eda926 100644 --- a/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/view/animation/AnimationProvider.java +++ b/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/view/animation/AnimationProvider.java @@ -19,15 +19,16 @@ package org.geometerplus.zlibrary.ui.android.view.animation; -import java.util.LinkedList; -import java.util.List; - -import android.graphics.*; -import android.util.FloatMath; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; import org.geometerplus.zlibrary.core.library.ZLibrary; import org.geometerplus.zlibrary.core.view.ZLViewEnums; +import java.util.LinkedList; +import java.util.List; + public abstract class AnimationProvider { public static enum Mode { NoScrolling(false), @@ -145,7 +146,7 @@ public final void startAnimatedScrolling(int x, int y, int speed) { final DrawInfo info1 = myDrawInfos.get(i); final float dX = info0.X - info1.X; final float dY = info0.Y - info1.Y; - velocity += FloatMath.sqrt(dX * dX + dY * dY) / Math.max(1, info1.Start - info0.Start); + velocity += Math.sqrt(dX * dX + dY * dY) / Math.max(1, info1.Start - info0.Start); } velocity /= myDrawInfos.size() - 1; velocity *= duration; diff --git a/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/view/animation/CurlAnimationProvider.java b/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/view/animation/CurlAnimationProvider.java index 918a676..f01472a 100644 --- a/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/view/animation/CurlAnimationProvider.java +++ b/fBReader/src/main/java/org/geometerplus/zlibrary/ui/android/view/animation/CurlAnimationProvider.java @@ -19,12 +19,13 @@ package org.geometerplus.zlibrary.ui.android.view.animation; -import android.graphics.*; -import android.util.FloatMath; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; import org.geometerplus.zlibrary.core.util.BitmapUtil; import org.geometerplus.zlibrary.core.view.ZLViewEnums; - import org.geometerplus.zlibrary.ui.android.util.ZLAndroidColorUtil; import org.geometerplus.zlibrary.ui.android.view.ViewUtil; @@ -122,7 +123,7 @@ private void drawInternalNoHack(Canvas canvas) { { float d1 = x - x1; float d2 = y - cornerY; - sX = FloatMath.sqrt(d1 * d1 + d2 * d2) / 2; + sX = (float) (Math.sqrt(d1 * d1 + d2 * d2) / 2); if (cornerX == 0) { sX = -sX; } @@ -130,7 +131,7 @@ private void drawInternalNoHack(Canvas canvas) { { float d1 = x - cornerX; float d2 = y - y1; - sY = FloatMath.sqrt(d1 * d1 + d2 * d2) / 2; + sY = (float) (Math.sqrt(d1 * d1 + d2 * d2) / 2); if (cornerY == 0) { sY = -sY; } diff --git a/fBReader/src/main/res/layout/dialog_symbol.xml b/fBReader/src/main/res/layout/dialog_symbol.xml new file mode 100644 index 0000000..ca20133 --- /dev/null +++ b/fBReader/src/main/res/layout/dialog_symbol.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/fBReader/src/main/res/layout/dialog_translate.xml b/fBReader/src/main/res/layout/dialog_translate.xml new file mode 100644 index 0000000..a82c943 --- /dev/null +++ b/fBReader/src/main/res/layout/dialog_translate.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fBReader/src/main/res/layout/main_tranlate_web.xml b/fBReader/src/main/res/layout/main_tranlate_web.xml new file mode 100644 index 0000000..d0aa8c3 --- /dev/null +++ b/fBReader/src/main/res/layout/main_tranlate_web.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/fBReader/src/main/res/values/dimens.xml b/fBReader/src/main/res/values/dimens.xml new file mode 100644 index 0000000..b7fec3a --- /dev/null +++ b/fBReader/src/main/res/values/dimens.xml @@ -0,0 +1,4 @@ + + + 12dp + diff --git a/fBReader/src/test/java/org/geometerplus/android/fbreader/FBReaderTest.java b/fBReader/src/test/java/org/geometerplus/android/fbreader/FBReaderTest.java new file mode 100644 index 0000000..fa32671 --- /dev/null +++ b/fBReader/src/test/java/org/geometerplus/android/fbreader/FBReaderTest.java @@ -0,0 +1,20 @@ +package org.geometerplus.android.fbreader; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class FBReaderTest { + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + @Test + public void testConverter(){ + + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f2807c1..baf5eae 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ -#Wed Mar 15 14:57:24 CST 2017 +#Tue Oct 23 06:44:08 CST 2018 Android.useDeprecatedNdk=true distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/library/build.gradle b/library/build.gradle index bdd746b..6cf3c96 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' android { compileSdkVersion 19 - buildToolsVersion "25.0.1" + buildToolsVersion '26.0.2' defaultConfig { minSdkVersion 8 @@ -14,6 +14,11 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } + debug { + + } + sourceSets { + } } } diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..e69de29 diff --git a/settings.gradle b/settings.gradle index c78cda5..20847aa 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ -include ':ambilWarna' +include ':ambilWarna', ':translate_bd' include ':superToasts' include ':code' include ':library' diff --git a/superToasts/build.gradle b/superToasts/build.gradle index 1b593ab..356383b 100644 --- a/superToasts/build.gradle +++ b/superToasts/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' android { compileSdkVersion 19 - buildToolsVersion "25.0.1" + buildToolsVersion '26.0.2' defaultConfig { minSdkVersion 8 @@ -14,5 +14,10 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } + debug { + + } + sourceSets { + } } } diff --git a/translate_bd/.gitignore b/translate_bd/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/translate_bd/.gitignore @@ -0,0 +1 @@ +/build diff --git a/translate_bd/build.gradle b/translate_bd/build.gradle new file mode 100644 index 0000000..25ab318 --- /dev/null +++ b/translate_bd/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 27 + + + + defaultConfig { + minSdkVersion 14 + targetSdkVersion 27 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + debug {} + sourceSets {} + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) +} diff --git a/translate_bd/proguard-rules.pro b/translate_bd/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/translate_bd/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/translate_bd/src/androidTest/java/loveenbooks/dyg/com/translate_bd/ExampleInstrumentedTest.java b/translate_bd/src/androidTest/java/loveenbooks/dyg/com/translate_bd/ExampleInstrumentedTest.java new file mode 100644 index 0000000..9dd8585 --- /dev/null +++ b/translate_bd/src/androidTest/java/loveenbooks/dyg/com/translate_bd/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package loveenbooks.dyg.com.translate_bd; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("loveenbooks.dyg.com.translate_bd.test", appContext.getPackageName()); + } +} diff --git a/translate_bd/src/main/AndroidManifest.xml b/translate_bd/src/main/AndroidManifest.xml new file mode 100644 index 0000000..27728d9 --- /dev/null +++ b/translate_bd/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/translate_bd/src/main/java/com/baidu/translate/demo/HttpGet.java b/translate_bd/src/main/java/com/baidu/translate/demo/HttpGet.java new file mode 100755 index 0000000..316822c --- /dev/null +++ b/translate_bd/src/main/java/com/baidu/translate/demo/HttpGet.java @@ -0,0 +1,159 @@ +package com.baidu.translate.demo; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Map; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +class HttpGet { + protected static final int SOCKET_TIMEOUT = 10000; // 10S + protected static final String GET = "GET"; + + public static String get(String host, Map params) { + try { + // 设置SSLContext + SSLContext sslcontext = SSLContext.getInstance("TLS"); + sslcontext.init(null, new TrustManager[] { myX509TrustManager }, null); + + String sendUrl = getUrlWithQueryString(host, params); + + // System.out.println("URL:" + sendUrl); + + URL uri = new URL(sendUrl); // 创建URL对象 + HttpURLConnection conn = (HttpURLConnection) uri.openConnection(); + if (conn instanceof HttpsURLConnection) { + ((HttpsURLConnection) conn).setSSLSocketFactory(sslcontext.getSocketFactory()); + } + + conn.setConnectTimeout(SOCKET_TIMEOUT); // 设置相应超时 + conn.setRequestMethod(GET); + int statusCode = conn.getResponseCode(); + if (statusCode != HttpURLConnection.HTTP_OK) { + System.out.println("Http错误码:" + statusCode); + } + + // 读取服务器的数据 + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + StringBuilder builder = new StringBuilder(); + String line = null; + while ((line = br.readLine()) != null) { + builder.append(line); + } + + String text = builder.toString(); + + close(br); // 关闭数据流 + close(is); // 关闭数据流 + conn.disconnect(); // 断开连接 + + return text; + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (KeyManagementException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + + return null; + } + + public static String getUrlWithQueryString(String url, Map params) { + if (params == null) { + return url; + } + + StringBuilder builder = new StringBuilder(url); + if (url.contains("?")) { + builder.append("&"); + } else { + builder.append("?"); + } + + int i = 0; + for (String key : params.keySet()) { + String value = params.get(key); + if (value == null) { // 过滤空的key + continue; + } + + if (i != 0) { + builder.append('&'); + } + + builder.append(key); + builder.append('='); + builder.append(encode(value)); + + i++; + } + + return builder.toString(); + } + + protected static void close(Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * 对输入的字符串进行URL编码, 即转换为%20这种形式 + * + * @param input 原文 + * @return URL编码. 如果编码失败, 则返回原文 + */ + public static String encode(String input) { + if (input == null) { + return ""; + } + + try { + return URLEncoder.encode(input, "utf-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + return input; + } + + private static TrustManager myX509TrustManager = new X509TrustManager() { + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + }; + +} diff --git a/translate_bd/src/main/java/com/baidu/translate/demo/MD5.java b/translate_bd/src/main/java/com/baidu/translate/demo/MD5.java new file mode 100755 index 0000000..92fbb6a --- /dev/null +++ b/translate_bd/src/main/java/com/baidu/translate/demo/MD5.java @@ -0,0 +1,121 @@ +package com.baidu.translate.demo; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * MD5编码相关的类 + * + * @author wangjingtao + * + */ +public class MD5 { + // 首先初始化一个字符数组,用来存放每个16进制字符 + private static final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', + 'e', 'f' }; + + /** + * 获得一个字符串的MD5值 + * + * @param input 输入的字符串 + * @return 输入字符串的MD5值 + * + */ + public static String md5(String input) { + if (input == null) + return null; + + try { + // 拿到一个MD5转换器(如果想要SHA1参数换成”SHA1”) + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + // 输入的字符串转换成字节数组 + byte[] inputByteArray = input.getBytes("utf-8"); + // inputByteArray是输入字符串转换得到的字节数组 + messageDigest.update(inputByteArray); + // 转换并返回结果,也是字节数组,包含16个元素 + byte[] resultByteArray = messageDigest.digest(); + // 字符数组转换成字符串返回 + return byteArrayToHex(resultByteArray); + } catch (Throwable e) { + return null; + } + } + + /** + * 获取文件的MD5值 + * + * @param file + * @return + */ + public static String md5(File file) { + try { + if (!file.isFile()) { + System.err.println("文件" + file.getAbsolutePath() + "不存在或者不是文件"); + return null; + } + + FileInputStream in = new FileInputStream(file); + + String result = md5(in); + + in.close(); + + return result; + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } + + public static String md5(InputStream in) { + + try { + MessageDigest messagedigest = MessageDigest.getInstance("MD5"); + + byte[] buffer = new byte[1024]; + int read = 0; + while ((read = in.read(buffer)) != -1) { + messagedigest.update(buffer, 0, read); + } + + in.close(); + + String result = byteArrayToHex(messagedigest.digest()); + + return result; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } + + private static String byteArrayToHex(byte[] byteArray) { + // new一个字符数组,这个就是用来组成结果字符串的(解释一下:一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方)) + char[] resultCharArray = new char[byteArray.length * 2]; + // 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去 + int index = 0; + for (byte b : byteArray) { + resultCharArray[index++] = hexDigits[b >>> 4 & 0xf]; + resultCharArray[index++] = hexDigits[b & 0xf]; + } + + // 字符数组组合成字符串返回 + return new String(resultCharArray); + + } + +} diff --git a/translate_bd/src/main/java/com/baidu/translate/demo/OnGetResult.java b/translate_bd/src/main/java/com/baidu/translate/demo/OnGetResult.java new file mode 100644 index 0000000..b186e5f --- /dev/null +++ b/translate_bd/src/main/java/com/baidu/translate/demo/OnGetResult.java @@ -0,0 +1,5 @@ +package com.baidu.translate.demo; + +public interface OnGetResult { + void onGetResult(String result); +} diff --git a/translate_bd/src/main/java/com/baidu/translate/demo/TransApi.java b/translate_bd/src/main/java/com/baidu/translate/demo/TransApi.java new file mode 100755 index 0000000..d7bd7c4 --- /dev/null +++ b/translate_bd/src/main/java/com/baidu/translate/demo/TransApi.java @@ -0,0 +1,41 @@ +package com.baidu.translate.demo; + +import java.util.HashMap; +import java.util.Map; + +public class TransApi { + private static final String TRANS_API_HOST = "http://api.fanyi.baidu.com/api/trans/vip/translate"; + + private String appid; + private String securityKey; + + public TransApi(String appid, String securityKey) { + this.appid = appid; + this.securityKey = securityKey; + } + + public String getTransResult(String query, String from, String to) { + Map params = buildParams(query, from, to); + return HttpGet.get(TRANS_API_HOST, params); + } + + private Map buildParams(String query, String from, String to) { + Map params = new HashMap(); + params.put("q", query); + params.put("from", from); + params.put("to", to); + + params.put("appid", appid); + + // 随机数 + String salt = String.valueOf(System.currentTimeMillis()); + params.put("salt", salt); + + // 签名 + String src = appid + query + salt + securityKey; // 加密前的原文 + params.put("sign", MD5.md5(src)); + + return params; + } + +} diff --git a/translate_bd/src/main/java/com/baidu/translate/demo/TransUtils.java b/translate_bd/src/main/java/com/baidu/translate/demo/TransUtils.java new file mode 100644 index 0000000..879c63c --- /dev/null +++ b/translate_bd/src/main/java/com/baidu/translate/demo/TransUtils.java @@ -0,0 +1,45 @@ +package com.baidu.translate.demo; + +public class TransUtils { + + // 在平台申请的APP_ID 详见 http://api.fanyi.baidu.com/api/trans/product/desktop?req=developer + private static final String APP_ID = "20181022000223183"; + private static final String SECURITY_KEY = "CS5vZa3BKyz9NqG29X4S"; + + public static void requestNet(final String src_word, final OnGetResult listener) { + new Thread(new Runnable() { + @Override + public void run() { + TransApi api = new TransApi(APP_ID, SECURITY_KEY); + listener.onGetResult(decode(api.getTransResult(src_word, "auto", "zh"))); + + } + }).start(); + } + + private static String decode(String unicodeStr) { + if (unicodeStr == null) { + return null; + } + StringBuffer retBuf = new StringBuffer(); + int maxLoop = unicodeStr.length(); + for (int i = 0; i < maxLoop; i++) { + if (unicodeStr.charAt(i) == '\\') { + if ((i < maxLoop - 5) && ((unicodeStr.charAt(i + 1) == 'u') || (unicodeStr.charAt(i + 1) == 'U'))) + try { + retBuf.append((char) Integer.parseInt(unicodeStr.substring(i + 2, i + 6), 16)); + i += 5; + } catch (NumberFormatException localNumberFormatException) { + retBuf.append(unicodeStr.charAt(i)); + } + else + retBuf.append(unicodeStr.charAt(i)); + } else { + retBuf.append(unicodeStr.charAt(i)); + } + } + return retBuf.toString(); + } + + +} diff --git a/translate_bd/src/main/res/values/strings.xml b/translate_bd/src/main/res/values/strings.xml new file mode 100644 index 0000000..6256683 --- /dev/null +++ b/translate_bd/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + translate_bd + diff --git a/translate_bd/src/test/java/loveenbooks/dyg/com/translate_bd/ExampleUnitTest.java b/translate_bd/src/test/java/loveenbooks/dyg/com/translate_bd/ExampleUnitTest.java new file mode 100644 index 0000000..c6fada4 --- /dev/null +++ b/translate_bd/src/test/java/loveenbooks/dyg/com/translate_bd/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package loveenbooks.dyg.com.translate_bd; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file