responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return value -> {
byte[] responseBytes = UTF8BOMFighter.removeUTF8BOM(value.bytes());
if (!TextUtils.isEmpty(encode)) {
try {
return new String((responseBytes), Charset.forName(encode));
} catch (Exception ignored) {
}
}
String charsetStr;
MediaType mediaType = value.contentType();
//根据http头判断
if (mediaType != null) {
Charset charset = mediaType.charset();
if (charset != null) {
return new String((responseBytes), charset);
}
}
//根据内容判断
charsetStr = EncodingDetect.getEncodeInHtml(responseBytes);
return new String(responseBytes, Charset.forName(charsetStr));
};
}
}
================================================
FILE: app/src/main/java/com/kunfei/bookshelf/help/ExoPlayerHelper.kt
================================================
package com.kunfei.bookshelf.help
import android.net.Uri
import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.source.dash.DashMediaSource
import com.google.android.exoplayer2.source.hls.HlsMediaSource
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource
import com.google.android.exoplayer2.util.Util.inferContentType
import com.kunfei.bookshelf.base.BaseModelImpl
object ExoPlayerHelper {
fun createMediaSource(uri: Uri, overrideExtension: String?): MediaSource {
val mediaItem = MediaItem.fromUri(uri)
val dataSourceFactory = OkHttpDataSource.Factory(BaseModelImpl.getClient())
val mediaSourceFactory = when (inferContentType(uri, overrideExtension)) {
C.TYPE_SS -> SsMediaSource.Factory(dataSourceFactory)
C.TYPE_DASH -> DashMediaSource.Factory(dataSourceFactory)
C.TYPE_HLS -> HlsMediaSource.Factory(dataSourceFactory)
else -> ProgressiveMediaSource.Factory(dataSourceFactory)
}
return mediaSourceFactory.createMediaSource(mediaItem)
}
}
================================================
FILE: app/src/main/java/com/kunfei/bookshelf/help/FileHelp.java
================================================
package com.kunfei.bookshelf.help;
import android.os.Environment;
import com.kunfei.bookshelf.MApplication;
import com.kunfei.bookshelf.utils.IOUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.Single;
/**
* Created by newbiechen on 17-5-11.
*/
@SuppressWarnings("ALL")
public class FileHelp {
public static final byte BLANK = 0x0a;
//采用自己的格式去设置文件,防止文件被系统文件查询到
public static final String SUFFIX_NB = ".nb";
public static final String SUFFIX_TXT = ".txt";
public static final String SUFFIX_EPUB = ".epub";
public static final String SUFFIX_PDF = ".pdf";
//获取文件夹
public static File getFolder(String filePath) {
File file = new File(filePath);
//如果文件夹不存在,就创建它
if (!file.exists()) {
file.mkdirs();
}
return file;
}
//获取文件
public static synchronized File createFileIfNotExist(String filePath) {
File file = new File(filePath);
try {
if (!file.exists()) {
//创建父类文件夹
getFolder(file.getParent());
//创建文件
file.createNewFile();
}
} catch (IOException e) {
}
return file;
}
//获取Cache文件夹
public static String getFilesPath() {
if (isSdCardExist()) {
try {
return MApplication.getInstance()
.getExternalFilesDir(null)
.getAbsolutePath();
} catch (Exception ignored) {
}
}
return MApplication.getInstance()
.getFilesDir()
.getAbsolutePath();
}
//获取Cache文件夹
public static String getCachePath() {
if (isSdCardExist()) {
try {
return MApplication.getInstance()
.getExternalCacheDir()
.getAbsolutePath();
} catch (Exception ignored) {
}
}
return MApplication.getInstance()
.getCacheDir()
.getAbsolutePath();
}
public static long getDirSize(File file) {
//判断文件是否存在
if (file.exists()) {
//如果是目录则递归计算其内容的总大小
if (file.isDirectory()) {
File[] children = file.listFiles();
long size = 0;
for (File f : children)
size += getDirSize(f);
return size;
} else {
return file.length();
}
} else {
return 0;
}
}
public static String getFileSize(long size) {
if (size <= 0) return "0";
final String[] units = new String[]{"b", "kb", "M", "G", "T"};
//计算单位的,原理是利用lg,公式是 lg(1024^n) = nlg(1024),最后 nlg(1024)/lg(1024) = n。
int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
//计算原理是,size/单位值。单位值指的是:比如说b = 1024,KB = 1024^2
return new DecimalFormat("#,##0.##").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
}
/**
* 本来是获取File的内容的。但是为了解决文本缩进、换行的问题
* 这个方法就是专门用来获取书籍的...
*
* 应该放在BookRepository中。。。
*
* @param file
* @return
*/
public static String getFileContent(File file) {
Reader reader = null;
String str = null;
StringBuilder sb = new StringBuilder();
try {
reader = new FileReader(file);
BufferedReader br = new BufferedReader(reader);
while ((str = br.readLine()) != null) {
//过滤空语句
if (!str.equals("")) {
//由于sb会自动过滤\n,所以需要加上去
sb.append(" " + str + "\n");
}
}
} catch (FileNotFoundException e) {
} catch (IOException e) {
} finally {
IOUtils.close(reader);
}
return sb.toString();
}
//判断是否挂载了SD卡
public static boolean isSdCardExist() {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
return true;
}
return false;
}
//递归删除文件夹下的数据
public static synchronized void deleteFile(String filePath) {
File file = new File(filePath);
if (!file.exists()) return;
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File subFile : files) {
String path = subFile.getPath();
deleteFile(path);
}
}
//删除文件
file.delete();
}
//由于递归的耗时问题,取巧只遍历内部三层
//获取txt文件
public static List getTxtFiles(String filePath, int layer) {
List txtFiles = new ArrayList();
File file = new File(filePath);
//如果层级为 3,则直接返回
if (layer == 3) {
return txtFiles;
}
//获取文件夹
File[] dirs = file.listFiles(
pathname -> {
if (pathname.isDirectory() && !pathname.getName().startsWith(".")) {
return true;
}
//获取txt文件
else if (pathname.getName().endsWith(".txt")) {
txtFiles.add(pathname);
return false;
} else {
return false;
}
}
);
//遍历文件夹
for (File dir : dirs) {
//递归遍历txt文件
txtFiles.addAll(getTxtFiles(dir.getPath(), layer + 1));
}
return txtFiles;
}
//由于遍历比较耗时
public static Single> getSDTxtFile() {
//外部存储卡路径
String rootPath = Environment.getExternalStorageDirectory().getPath();
return Single.create(e -> {
List files = getTxtFiles(rootPath, 0);
e.onSuccess(files);
});
}
public static String getFileSuffix(String filePath) {
File file = new File(filePath);
return getFileSuffix(file);
}
public static String getFileSuffix(File file) {
if (file == null || !file.exists() || file.isDirectory()) {
return "";
}
String fileName = file.getName();
int dotIndex = fileName.lastIndexOf(".");
return dotIndex > 0 ? fileName.substring(dotIndex) : "";
}
public static void createFolderIfNotExists(String path) {
File folder = new File(path);
if (!folder.exists()) {
folder.mkdirs();
}
}
}
================================================
FILE: app/src/main/java/com/kunfei/bookshelf/help/IntentData.kt
================================================
package com.kunfei.bookshelf.help
object IntentData {
private val bigData: MutableMap = mutableMapOf()
@Synchronized
fun put(key: String, data: Any?) {
data?.let {
bigData[key] = data
}
}
@Synchronized
fun put(data: Any?): String {
val key = System.currentTimeMillis().toString()
data?.let {
bigData[key] = data
}
return key
}
@Suppress("UNCHECKED_CAST")
@Synchronized
fun get(key: String?): T? {
if (key == null) return null
val data = bigData[key]
bigData.remove(key)
return data as? T
}
}
================================================
FILE: app/src/main/java/com/kunfei/bookshelf/help/ItemTouchCallback.java
================================================
package com.kunfei.bookshelf.help;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.viewpager.widget.ViewPager;
/**
* Created by GKF on 2018/3/16.
*/
public class ItemTouchCallback extends ItemTouchHelper.Callback {
private SwipeRefreshLayout swipeRefreshLayout;
private ViewPager viewPager;
public void setSwipeRefreshLayout(SwipeRefreshLayout swipeRefreshLayout) {
this.swipeRefreshLayout = swipeRefreshLayout;
}
public void setViewPager(ViewPager viewPager) {
this.viewPager = viewPager;
}
/**
* Item操作的回调
*/
private OnItemTouchCallbackListener onItemTouchCallbackListener;
/**
* 是否可以拖拽
*/
private boolean isCanDrag = false;
/**
* 是否可以被滑动
*/
private boolean isCanSwipe = false;
/**
* 设置Item操作的回调,去更新UI和数据源
*/
public void setOnItemTouchCallbackListener(OnItemTouchCallbackListener onItemTouchCallbackListener) {
this.onItemTouchCallbackListener = onItemTouchCallbackListener;
}
/**
* 设置是否可以被拖拽
*
* @param canDrag 是true,否false
*/
public void setDragEnable(boolean canDrag) {
isCanDrag = canDrag;
}
/**
* 设置是否可以被滑动
*
* @param canSwipe 是true,否false
*/
public void setSwipeEnable(boolean canSwipe) {
isCanSwipe = canSwipe;
}
/**
* 当Item被长按的时候是否可以被拖拽
*/
@Override
public boolean isLongPressDragEnabled() {
return isCanDrag;
}
/**
* Item是否可以被滑动(H:左右滑动,V:上下滑动)
*/
@Override
public boolean isItemViewSwipeEnabled() {
return isCanSwipe;
}
/**
* 当用户拖拽或者滑动Item的时候需要我们告诉系统滑动或者拖拽的方向
*/
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {// GridLayoutManager
// flag如果值是0,相当于这个功能被关闭
int dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlag = 0;
// create make
return makeMovementFlags(dragFlag, swipeFlag);
} else if (layoutManager instanceof LinearLayoutManager) {// linearLayoutManager
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
int orientation = linearLayoutManager.getOrientation();
int dragFlag = 0;
int swipeFlag = 0;
// 为了方便理解,相当于分为横着的ListView和竖着的ListView
if (orientation == LinearLayoutManager.HORIZONTAL) {// 如果是横向的布局
swipeFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
} else if (orientation == LinearLayoutManager.VERTICAL) {// 如果是竖向的布局,相当于ListView
dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
swipeFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
}
return makeMovementFlags(dragFlag, swipeFlag);
}
return 0;
}
/**
* 当Item被拖拽的时候被回调
*
* @param recyclerView recyclerView
* @param srcViewHolder 拖拽的ViewHolder
* @param targetViewHolder 目的地的viewHolder
*/
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder srcViewHolder, @NonNull RecyclerView.ViewHolder targetViewHolder) {
if (onItemTouchCallbackListener != null) {
return onItemTouchCallbackListener.onMove(srcViewHolder.getAdapterPosition(), targetViewHolder.getAdapterPosition());
}
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
if (onItemTouchCallbackListener != null) {
onItemTouchCallbackListener.onSwiped(viewHolder.getAdapterPosition());
}
}
@Override
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
final boolean swiping = actionState == ItemTouchHelper.ACTION_STATE_DRAG;
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setEnabled(!swiping);
}
if (viewPager != null) {
viewPager.requestDisallowInterceptTouchEvent(swiping);
}
}
public interface OnItemTouchCallbackListener {
/**
* 当某个Item被滑动删除的时候
*
* @param adapterPosition item的position
*/
void onSwiped(int adapterPosition);
/**
* 当两个Item位置互换的时候被回调
*
* @param srcPosition 拖拽的item的position
* @param targetPosition 目的地的Item的position
* @return 开发者处理了操作应该返回true,开发者没有处理就返回false
*/
boolean onMove(int srcPosition, int targetPosition);
}
}
================================================
FILE: app/src/main/java/com/kunfei/bookshelf/help/JsExtensions.java
================================================
package com.kunfei.bookshelf.help;
import com.kunfei.bookshelf.DbHelper;
import com.kunfei.bookshelf.base.BaseModelImpl;
import com.kunfei.bookshelf.bean.CookieBean;
import com.kunfei.bookshelf.constant.AppConst;
import com.kunfei.bookshelf.model.analyzeRule.AnalyzeHeaders;
import com.kunfei.bookshelf.model.analyzeRule.AnalyzeUrl;
import com.kunfei.bookshelf.utils.MD5Utils;
import com.kunfei.bookshelf.utils.StringUtils;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import java.io.IOException;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import retrofit2.Response;
@SuppressWarnings({"unused", "WeakerAccess"})
public interface JsExtensions {
/**
* js实现跨域访问,不能删
*/
default String ajax(String urlStr) {
try {
AnalyzeUrl analyzeUrl = new AnalyzeUrl(urlStr, AnalyzeHeaders.getDefaultHeader());
Response response = BaseModelImpl.getInstance().getResponseO(analyzeUrl)
.blockingFirst();
return response.body();
} catch (Exception e) {
return e.getLocalizedMessage();
}
}
/**
* js实现跨域访问,不能删
*/
default Response getResponse(String urlStr) {
try {
AnalyzeUrl analyzeUrl = new AnalyzeUrl(urlStr, AnalyzeHeaders.getDefaultHeader());
return BaseModelImpl.getInstance().getResponseO(analyzeUrl)
.blockingFirst();
} catch (Exception e) {
return Response.success(e.getLocalizedMessage());
}
}
/**
* js实现解码,不能删
*/
default String base64Decoder(String base64) {
return StringUtils.base64Decode(base64);
}
/**
* 章节数转数字
*/
default String toNumChapter(String s) {
if (s == null) {
return null;
}
Pattern pattern = Pattern.compile("(第)(.+?)(章)");
Matcher matcher = pattern.matcher(s);
if (matcher.find()) {
return matcher.group(1) + StringUtils.stringToInt(matcher.group(2)) + matcher.group(3);
}
return s;
}
/**
* js实现重定向拦截,不能删
*/
default Connection.Response get(String urlStr, Map headers) throws IOException {
return Jsoup.connect(urlStr)
.sslSocketFactory(SSLSocketClient.getSSLSocketFactory())
.ignoreContentType(true)
.followRedirects(false)
.headers(headers)
.method(Connection.Method.GET)
.execute();
}
/**
* js实现重定向拦截,不能删
*/
default Connection.Response post(String urlStr, String body, Map headers) throws IOException {
return Jsoup.connect(urlStr)
.sslSocketFactory(SSLSocketClient.getSSLSocketFactory())
.ignoreContentType(true)
.followRedirects(false)
.requestBody(body)
.headers(headers)
.method(Connection.Method.POST)
.execute();
}
default void putCache(String key, String value) {
CookieBean cookie = new CookieBean(key, value);
DbHelper.getDaoSession().getCookieBeanDao().insertOrReplace(cookie);
}
default String getCache(String key) {
CookieBean cookie = DbHelper.getDaoSession().getCookieBeanDao().load(key);
if (cookie == null) {
return null;
}
return cookie.getCookie();
}
default String md5Encode(String text) {
return MD5Utils.strToMd5By32(text);
}
default String androidId() {
return AppConst.INSTANCE.getAndroidId();
}
}
================================================
FILE: app/src/main/java/com/kunfei/bookshelf/help/LauncherIcon.java
================================================
package com.kunfei.bookshelf.help;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import com.kunfei.bookshelf.MApplication;
import com.kunfei.bookshelf.R;
/**
* Created by GKF on 2018/2/27.
* 更换图标
*/
public class LauncherIcon {
private static PackageManager packageManager = MApplication.getInstance().getPackageManager();
private static ComponentName componentNameMain = new ComponentName(MApplication.getInstance(), "com.kunfei.bookshelf.view.activity.WelcomeActivity");
private static ComponentName componentNameBookMain = new ComponentName(MApplication.getInstance(), "com.kunfei.bookshelf.view.activity.WelcomeBookActivity");
public static void ChangeIcon(String icon) {
if (icon.equals(MApplication.getInstance().getString(R.string.icon_book))) {
if (packageManager.getComponentEnabledSetting(componentNameBookMain) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
//启用
packageManager.setComponentEnabledSetting(componentNameBookMain,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
//禁用
packageManager.setComponentEnabledSetting(componentNameMain,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}
} else {
if (packageManager.getComponentEnabledSetting(componentNameMain) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
//启用
packageManager.setComponentEnabledSetting(componentNameMain,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
//禁用
packageManager.setComponentEnabledSetting(componentNameBookMain,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}
}
}
public static String getInUseIcon() {
if (packageManager.getComponentEnabledSetting(componentNameBookMain) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
return MApplication.getInstance().getString(R.string.icon_book);
}
return MApplication.getInstance().getString(R.string.icon_main);
}
}
================================================
FILE: app/src/main/java/com/kunfei/bookshelf/help/MediaManager.java
================================================
package com.kunfei.bookshelf.help;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import com.kunfei.bookshelf.MApplication;
import com.kunfei.bookshelf.R;
/**
* Created by GKF on 2018/1/9.
* 播放音频
*/
public class MediaManager {
private static int VOLUME;
private AudioManager audioManager;
private int stream;
private final int FADE_DURATION = 1000;
private final int FADE_INTERVAL = 100;
private boolean isFading = false;
private boolean cancelFading = false;
public static MediaManager instance;
private MediaManager() {
audioManager = (AudioManager) MApplication.getInstance().getSystemService(Context.AUDIO_SERVICE);
}
public void setStream(int stream) {
this.stream = stream;
getSysVolume();
}
public static synchronized MediaManager getInstance() {
if (instance == null)
instance = new MediaManager();
return instance;
}
public void fadeInVolume() {
if (!isFading) {
getSysVolume();
} else {
cancelFading = true;
}
while (isFading) try {
Thread.sleep(10);
} catch (Exception ignored) {
}
cancelFading = false;
startAudioFade(1, VOLUME);
setSysVolume(VOLUME);
}
public void fadeOutVolume() {
if (!isFading) {
getSysVolume();
} else {
cancelFading = true;
}
while (isFading) try {
Thread.sleep(FADE_INTERVAL);
} catch (Exception ignored) {
}
cancelFading = false;
startAudioFade(VOLUME, 1);
setSysVolume(VOLUME);
}
private void getSysVolume() {
VOLUME = audioManager.getStreamVolume(stream);
}
private void setSysVolume(float vol) {
audioManager.setStreamVolume(stream, (int) vol, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
}
private void startAudioFade(float from, float to) {
isFading = true;
cancelFading = false;
int numberOfSteps = FADE_DURATION / FADE_INTERVAL;
float deltaVolume = (to - from) / numberOfSteps;
for (float vol = from; (vol - to) * (vol - from) <= 0 && !cancelFading; vol += deltaVolume) {
setSysVolume(vol);
try {
Thread.sleep(FADE_INTERVAL);
} catch (Exception ignored) {
}
}
isFading = false;
cancelFading = false;
}
public static void playSilentSound(Context mContext) {
try {
// Stupid Android 8 "Oreo" hack to make media buttons work
MediaPlayer mMediaPlayer = MediaPlayer.create(mContext, R.raw.silent_sound);
mMediaPlayer.setOnCompletionListener(MediaPlayer::release);
mMediaPlayer.start();
} catch (Exception ignored) {
}
}
}
================================================
FILE: app/src/main/java/com/kunfei/bookshelf/help/ProcessTextHelp.java
================================================
package com.kunfei.bookshelf.help;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import com.kunfei.bookshelf.MApplication;
public class ProcessTextHelp {
private static PackageManager packageManager = MApplication.getInstance().getPackageManager();
private static ComponentName componentName = new ComponentName(MApplication.getInstance(), "com.kunfei.bookshelf.view.activity.ReceivingSharedActivity");
public static boolean isProcessTextEnabled() {
return packageManager.getComponentEnabledSetting(componentName) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
}
public static void setProcessTextEnable(boolean enable) {
if (enable) {
packageManager.setComponentEnabledSetting(componentName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
} else {
packageManager.setComponentEnabledSetting(componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}
}
}
================================================
FILE: app/src/main/java/com/kunfei/bookshelf/help/ReadBookControl.java
================================================
//Copyright (c) 2017. 章钦豪. All rights reserved.
package com.kunfei.bookshelf.help;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.util.DisplayMetrics;
import com.kunfei.bookshelf.MApplication;
import com.kunfei.bookshelf.utils.BitmapUtil;
import com.kunfei.bookshelf.utils.MeUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.kunfei.bookshelf.widget.page.PageLoader.DEFAULT_MARGIN_WIDTH;
public class ReadBookControl {
private static final int DEFAULT_BG = 1;
private int textDrawableIndex = DEFAULT_BG;
private List