添加文件系统

pull/5/head
fengyuecanzhu 4 years ago
parent 08b2194769
commit 5f73042209
  1. 23
      app/src/main/AndroidManifest.xml
  2. 6
      app/src/main/assets/updatelog.fy
  3. 2
      app/src/main/java/xyz/fycz/myreader/backup/BackupAndRestore.java
  4. 4
      app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java
  5. 127
      app/src/main/java/xyz/fycz/myreader/base/BaseActivity2.java
  6. 104
      app/src/main/java/xyz/fycz/myreader/base/BaseFragment.java
  7. 154
      app/src/main/java/xyz/fycz/myreader/base/BaseListAdapter.java
  8. 86
      app/src/main/java/xyz/fycz/myreader/base/BaseTabActivity.java
  9. 19
      app/src/main/java/xyz/fycz/myreader/base/BaseViewHolder.java
  10. 16
      app/src/main/java/xyz/fycz/myreader/base/IViewHolder.java
  11. 43
      app/src/main/java/xyz/fycz/myreader/base/ViewHolderImpl.java
  12. 4
      app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
  13. 2
      app/src/main/java/xyz/fycz/myreader/controller/SyncChaptersController.java
  14. 13
      app/src/main/java/xyz/fycz/myreader/creator/DialogCreator.java
  15. 13
      app/src/main/java/xyz/fycz/myreader/greendao/service/BookMarkService.java
  16. 36
      app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java
  17. 11
      app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java
  18. 12
      app/src/main/java/xyz/fycz/myreader/greendao/service/SearchHistoryService.java
  19. 2
      app/src/main/java/xyz/fycz/myreader/ui/bookinfo/BookInfoPresenter.java
  20. 87
      app/src/main/java/xyz/fycz/myreader/ui/filesys/BaseFileFragment.java
  21. 176
      app/src/main/java/xyz/fycz/myreader/ui/filesys/FileCategoryFragment.java
  22. 103
      app/src/main/java/xyz/fycz/myreader/ui/filesys/FileHolder.java
  23. 266
      app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemActivity.java
  24. 148
      app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemAdapter.java
  25. 89
      app/src/main/java/xyz/fycz/myreader/ui/filesys/LocalBookFragment.java
  26. 18
      app/src/main/java/xyz/fycz/myreader/ui/font/FontsPresenter.java
  27. 23
      app/src/main/java/xyz/fycz/myreader/ui/home/MainActivity.java
  28. 3
      app/src/main/java/xyz/fycz/myreader/ui/home/MainPresenter.java
  29. 4
      app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseAdapter.java
  30. 7
      app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseDetailedAdapter.java
  31. 45
      app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcasePresenter.java
  32. 6
      app/src/main/java/xyz/fycz/myreader/ui/read/ReadPresenter.java
  33. 7
      app/src/main/java/xyz/fycz/myreader/ui/read/catalog/CatalogActivityPresenter.java
  34. 2
      app/src/main/java/xyz/fycz/myreader/ui/read/catalog/CatalogPresenter.java
  35. 2
      app/src/main/java/xyz/fycz/myreader/ui/search/SearchBookPrensenter.java
  36. 19
      app/src/main/java/xyz/fycz/myreader/ui/user/LoginActivity.java
  37. 84
      app/src/main/java/xyz/fycz/myreader/ui/user/LoginPresenter.java
  38. 17
      app/src/main/java/xyz/fycz/myreader/ui/user/RegisterActivity.java
  39. 4
      app/src/main/java/xyz/fycz/myreader/ui/user/RegisterPresenter.java
  40. 49
      app/src/main/java/xyz/fycz/myreader/util/FileStack.java
  41. 272
      app/src/main/java/xyz/fycz/myreader/util/ImageUtil.java
  42. 71
      app/src/main/java/xyz/fycz/myreader/util/StatusBarCompat.java
  43. 30
      app/src/main/java/xyz/fycz/myreader/util/media/LoaderCreator.java
  44. 137
      app/src/main/java/xyz/fycz/myreader/util/media/LocalFileLoader.java
  45. 68
      app/src/main/java/xyz/fycz/myreader/util/media/MediaStoreHelper.java
  46. 93
      app/src/main/java/xyz/fycz/myreader/widget/DividerItemDecoration.java
  47. 267
      app/src/main/java/xyz/fycz/myreader/widget/RefreshLayout.java
  48. 4
      app/src/main/java/xyz/fycz/myreader/widget/page/PageView.java
  49. 6
      app/src/main/res/color/selector_btn_file_add.xml
  50. 6
      app/src/main/res/color/selector_btn_file_delete.xml
  51. 5
      app/src/main/res/color/selector_cb_file.xml
  52. BIN
      app/src/main/res/drawable-xhdpi/ic_back_last.png
  53. BIN
      app/src/main/res/drawable-xhdpi/ic_cb_file_normal.png
  54. BIN
      app/src/main/res/drawable-xhdpi/ic_cb_file_selected.png
  55. BIN
      app/src/main/res/drawable-xhdpi/ic_file_loaded.png
  56. BIN
      app/src/main/res/drawable-xhdpi/ic_no_data.png
  57. BIN
      app/src/main/res/drawable-xxhdpi/ic_menu_overflow.png
  58. 14
      app/src/main/res/drawable/ic_book_has.xml
  59. 10
      app/src/main/res/drawable/ic_folder.xml
  60. 5
      app/src/main/res/drawable/ic_tag_txt.xml
  61. 6
      app/src/main/res/drawable/selector_btn_add.xml
  62. 6
      app/src/main/res/drawable/selector_btn_delete.xml
  63. 6
      app/src/main/res/drawable/selector_cb_file.xml
  64. 10
      app/src/main/res/drawable/selector_common_bg.xml
  65. 6
      app/src/main/res/drawable/selector_pwd.xml
  66. 6
      app/src/main/res/drawable/shape_blue.xml
  67. 6
      app/src/main/res/drawable/shape_corner_black.xml
  68. 6
      app/src/main/res/drawable/shape_corner_blue.xml
  69. 6
      app/src/main/res/drawable/shape_unclick.xml
  70. 2
      app/src/main/res/layout/activity_book_info.xml
  71. 2
      app/src/main/res/layout/activity_catalog.xml
  72. 60
      app/src/main/res/layout/activity_file_system.xml
  73. 63
      app/src/main/res/layout/activity_login.xml
  74. 108
      app/src/main/res/layout/activity_register.xml
  75. 48
      app/src/main/res/layout/fragment_file_category.xml
  76. 12
      app/src/main/res/layout/fragment_local_book.xml
  77. 2
      app/src/main/res/layout/gridview_book_detailed_item.xml
  78. 2
      app/src/main/res/layout/gridview_book_item.xml
  79. 91
      app/src/main/res/layout/item_file.xml
  80. 2
      app/src/main/res/layout/listview_book_store_book_item.xml
  81. 28
      app/src/main/res/layout/scroll_title_indicator.xml
  82. 44
      app/src/main/res/layout/view_empty.xml
  83. 11
      app/src/main/res/layout/view_loading.xml
  84. 31
      app/src/main/res/layout/view_net_error.xml
  85. BIN
      app/src/main/res/mipmap-xhdpi/default_cover.jpg
  86. BIN
      app/src/main/res/mipmap-xhdpi/pwd_gone.png
  87. BIN
      app/src/main/res/mipmap-xhdpi/pwd_visiable.png
  88. 6
      app/src/main/res/values/attrs.xml
  89. 19
      app/src/main/res/values/colors.xml
  90. 1
      app/src/main/res/values/ids.xml
  91. 7
      app/src/main/res/values/strings.xml
  92. 52
      app/src/main/res/values/styles.xml
  93. 4
      app/version_code.properties

@ -27,6 +27,9 @@
<uses-permission android:name="android.permission.INTERNET"/> <!-- 查看网络连接 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <!-- 查看WLAN连接 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <!-- 修改或删除您的USB存储设备中的内容 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEM"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 读取您的USB存储设备中的内容 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <!-- 拨打电话 -->
<!-- <uses-permission android:name="android.permission.CALL_PHONE" /> -->
@ -87,15 +90,17 @@
</intent-filter>
</activity>
<activity android:name=".ui.font.FontsActivity">
</activity>
<activity android:name=".ui.user.LoginActivity">
</activity>
<activity android:name=".ui.user.RegisterActivity">
</activity>
<activity android:name=".ui.read.catalog.CatalogActivity">
</activity><activity android:name=".ui.about.AboutActivity">
</activity>
<activity android:name=".ui.font.FontsActivity"/>
<activity android:name=".ui.user.LoginActivity"/>
<activity android:name=".ui.user.RegisterActivity"/>
<activity android:name=".ui.read.catalog.CatalogActivity"/>
<activity android:name=".ui.about.AboutActivity"/>
<activity android:name=".ui.filesys.FileSystemActivity"/>
</application>
</manifest>

@ -1,3 +1,9 @@
2020.08.12
风月读书v1.20.081221
1、新增本地书籍导入页面(不再使用系统文件管理器导入),支持一次性导入多本书籍
2、新增智能导入(扫描)本地书籍
3、修复书架没有书籍时退出闪退的问题
2020.08.07
风月读书v1.20.080709
1、修复搜索书籍滑动混乱的问题

@ -18,7 +18,7 @@ import java.util.List;
public class BackupAndRestore {
BookService mBookService = new BookService();
BookService mBookService = BookService.getInstance();
/**
* 备份书架

@ -127,8 +127,4 @@ public class BaseActivity extends AppCompatActivity {
}
}

@ -0,0 +1,127 @@
package xyz.fycz.myreader.base;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.LayoutRes;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.util.StatusBarCompat;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public abstract class BaseActivity2 extends AppCompatActivity {
private static final int INVALID_VAL = -1;
protected CompositeDisposable mDisposable;
//ButterKnife
private Toolbar mToolbar;
private Unbinder unbinder;
/****************************abstract area*************************************/
@LayoutRes
protected abstract int getContentId();
/************************init area************************************/
protected void addDisposable(Disposable d){
if (mDisposable == null){
mDisposable = new CompositeDisposable();
}
mDisposable.add(d);
}
/**
* 配置Toolbar
* @param toolbar
*/
protected void setUpToolbar(Toolbar toolbar){
}
protected void initData(Bundle savedInstanceState){
}
/**
* 初始化零件
*/
protected void initWidget() {
}
/**
* 初始化点击事件
*/
protected void initClick(){
}
/**
* 逻辑使用区
*/
protected void processLogic(){
}
/*************************lifecycle area*****************************************************/
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentId());
initData(savedInstanceState);
unbinder = ButterKnife.bind(this);
initToolbar();
initWidget();
initClick();
processLogic();
}
private void initToolbar(){
//更严谨是通过反射判断是否存在Toolbar
mToolbar = findViewById(R.id.toolbar);
if (mToolbar != null){
supportActionBar(mToolbar);
setUpToolbar(mToolbar);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbinder.unbind();
if (mDisposable != null){
mDisposable.dispose();
}
}
/**************************used method area*******************************************/
protected void startActivity(Class<? extends AppCompatActivity> activity){
Intent intent = new Intent(this,activity);
startActivity(intent);
}
protected ActionBar supportActionBar(Toolbar toolbar){
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null){
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
}
mToolbar.setNavigationOnClickListener(
(v) -> finish()
);
return actionBar;
}
protected void setStatusBarColor(int statusColor){
StatusBarCompat.compat(this, ContextCompat.getColor(this, statusColor));
}
}

@ -0,0 +1,104 @@
package xyz.fycz.myreader.base;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.LayoutRes;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public abstract class BaseFragment extends Fragment {
protected CompositeDisposable mDisposable;
private View root = null;
private Unbinder unbinder;
@LayoutRes
protected abstract int getContentId();
/*******************************init area*********************************/
protected void addDisposable(Disposable d){
if (mDisposable == null){
mDisposable = new CompositeDisposable();
}
mDisposable.add(d);
}
protected void initData(Bundle savedInstanceState){
}
/**
* 初始化点击事件
*/
protected void initClick(){
}
/**
* 逻辑使用区
*/
protected void processLogic(){
}
/**
* 初始化零件
*/
protected void initWidget(Bundle savedInstanceState){
}
/******************************lifecycle area*****************************************/
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
int resId = getContentId();
root = inflater.inflate(resId,container,false);
return root;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initData(savedInstanceState);
unbinder = ButterKnife.bind(this,root);
initWidget(savedInstanceState);
initClick();
processLogic();
}
@Override
public void onDetach() {
super.onDetach();
unbinder.unbind();
if (mDisposable != null){
mDisposable.clear();
}
}
/**************************公共类*******************************************/
public String getName(){
return getClass().getName();
}
protected <VT> VT getViewById(int id){
if (root == null){
return null;
}
return (VT) root.findViewById(id);
}
}

@ -0,0 +1,154 @@
package xyz.fycz.myreader.base;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public abstract class BaseListAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private static final String TAG = "BaseListAdapter";
/*common statement*/
protected final List<T> mList = new ArrayList<>();
protected OnItemClickListener mClickListener;
protected OnItemLongClickListener mLongClickListener;
/************************abstract area************************/
protected abstract IViewHolder<T> createViewHolder(int viewType);
/*************************rewrite logic area***************************************/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
IViewHolder<T> viewHolder = createViewHolder(viewType);
View view = viewHolder.createItemView(parent);
//初始化
RecyclerView.ViewHolder holder = new BaseViewHolder(view, viewHolder);
return holder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//防止别人直接使用RecyclerView.ViewHolder调用该方法
if (!(holder instanceof BaseViewHolder))
throw new IllegalArgumentException("The ViewHolder item must extend BaseViewHolder");
IViewHolder<T> iHolder = ((BaseViewHolder) holder).holder;
iHolder.onBind(getItem(position),position);
//设置点击事件
holder.itemView.setOnClickListener((v)->{
if (mClickListener != null){
mClickListener.onItemClick(v,position);
}
//adapter监听点击事件
iHolder.onClick();
onItemClick(v,position);
});
//设置长点击事件
holder.itemView.setOnLongClickListener(
(v) -> {
boolean isClicked = false;
if (mLongClickListener != null){
isClicked = mLongClickListener.onItemLongClick(v,position);
}
//Adapter监听长点击事件
onItemLongClick(v,position);
return isClicked;
}
);
}
@Override
public int getItemCount() {
return mList.size();
}
protected void onItemClick(View v, int pos){
}
protected void onItemLongClick(View v, int pos){
}
/******************************public area***********************************/
public void setOnItemClickListener(OnItemClickListener mListener) {
this.mClickListener = mListener;
}
public void setOnItemLongClickListener(OnItemLongClickListener mListener){
this.mLongClickListener = mListener;
}
public void addItem(T value){
mList.add(value);
notifyDataSetChanged();
}
public void addItem(int index,T value){
mList.add(index, value);
notifyDataSetChanged();
}
public void addItems(List<T> values){
mList.addAll(values);
Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
}
});
}
public void removeItem(T value){
mList.remove(value);
notifyDataSetChanged();
}
public void removeItems(List<T> value){
mList.removeAll(value);
notifyDataSetChanged();
}
public T getItem(int position){
return mList.get(position);
}
public List<T> getItems(){
return Collections.unmodifiableList(mList);
}
public int getItemSize(){
return mList.size();
}
public void refreshItems(List<T> list){
mList.clear();
mList.addAll(list);
notifyDataSetChanged();
}
public void clear(){
mList.clear();
}
/***************************inner class area***********************************/
public interface OnItemClickListener{
void onItemClick(View view, int pos);
}
public interface OnItemLongClickListener{
boolean onItemLongClick(View view, int pos);
}
}

@ -0,0 +1,86 @@
package xyz.fycz.myreader.base;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import butterknife.BindView;
import com.google.android.material.tabs.TabLayout;
import xyz.fycz.myreader.R;
import java.util.List;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public abstract class BaseTabActivity extends BaseActivity2 {
/**************View***************/
@BindView(R.id.tab_tl_indicator)
protected TabLayout mTlIndicator;
@BindView(R.id.tab_vp)
protected ViewPager mVp;
/************Params*******************/
private List<Fragment> mFragmentList;
private List<String> mTitleList;
/**************abstract***********/
protected abstract List<Fragment> createTabFragments();
protected abstract List<String> createTabTitles();
/*****************rewrite method***************************/
@Override
protected void initWidget() {
super.initWidget();
setUpTabLayout();
}
private void setUpTabLayout(){
mFragmentList = createTabFragments();
mTitleList = createTabTitles();
checkParamsIsRight();
TabFragmentPageAdapter adapter = new TabFragmentPageAdapter(getSupportFragmentManager());
mVp.setAdapter(adapter);
mVp.setOffscreenPageLimit(3);
mTlIndicator.setupWithViewPager(mVp);
}
/**
* 检查输入的参数是否正确即Fragment和title是成对的
*/
private void checkParamsIsRight(){
if (mFragmentList == null || mTitleList == null){
throw new IllegalArgumentException("fragmentList or titleList doesn't have null");
}
if (mFragmentList.size() != mTitleList.size())
throw new IllegalArgumentException("fragment and title size must equal");
}
/******************inner class*****************/
class TabFragmentPageAdapter extends FragmentPagerAdapter {
public TabFragmentPageAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mTitleList.get(position);
}
}
}

@ -0,0 +1,19 @@
package xyz.fycz.myreader.base;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public class BaseViewHolder<T> extends RecyclerView.ViewHolder{
public IViewHolder<T> holder;
public BaseViewHolder(View itemView, IViewHolder<T> holder) {
super(itemView);
this.holder = holder;
holder.initView();
}
}

@ -0,0 +1,16 @@
package xyz.fycz.myreader.base;
import android.view.View;
import android.view.ViewGroup;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public interface IViewHolder<T> {
View createItemView(ViewGroup parent);
void initView();
void onBind(T data,int pos);
void onClick();
}

@ -0,0 +1,43 @@
package xyz.fycz.myreader.base;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public abstract class ViewHolderImpl<T> implements IViewHolder<T> {
private View view;
private Context context;
/****************************************************/
protected abstract int getItemLayoutId();
@Override
public View createItemView(ViewGroup parent) {
view = LayoutInflater.from(parent.getContext())
.inflate(getItemLayoutId(), parent, false);
context = parent.getContext();
return view;
}
protected <V extends View> V findById(int id){
return (V) view.findViewById(id);
}
protected Context getContext(){
return context;
}
protected View getItemView(){
return view;
}
@Override
public void onClick() {
}
}

@ -65,6 +65,10 @@ public class APPCONST {
public static final int SELECT_FILE_CODE = 10000;
public static final int PERMISSIONS_REQUEST_STORAGE = 10001;
//设置版本号
public static final int SETTING_VERSION = 1;
public static final String FORMAT_FILE_DATE = "yyyy-MM-dd";
}

@ -19,7 +19,7 @@ public class SyncChaptersController {
mLocalChapters = localChapters;
mNetChapters = netChapters;
mBook = book;
mChapterService = new ChapterService();
mChapterService = ChapterService.getInstance();
}
/**

@ -630,14 +630,11 @@ public class DialogCreator {
/* progressDialog.setPositiveButton("确定",positiveListener);
progressDialog.setNegativeButton("取消",negativeListener);*/
// 显示
MyApplication.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
progressDialog.show();
} catch (Exception e) {
e.printStackTrace();
}
MyApplication.runOnUiThread(() -> {
try {
progressDialog.show();
} catch (Exception e) {
e.printStackTrace();
}
});
return progressDialog;

@ -18,7 +18,18 @@ import java.util.List;
public class BookMarkService extends BaseService {
private static volatile BookMarkService sInstance;
public static BookMarkService getInstance() {
if (sInstance == null){
synchronized (BookMarkService.class){
if (sInstance == null){
sInstance = new BookMarkService();
}
}
}
return sInstance;
}
private ArrayList<BookMark> findBookMarks(String sql, String[] selectionArgs) {
ArrayList<BookMark> bookMarks = new ArrayList<>();
try {

@ -20,10 +20,21 @@ public class BookService extends BaseService {
private ChapterService mChapterService;
private BookMarkService mBookMarkService;
private static volatile BookService sInstance;
public BookService(){
mChapterService = new ChapterService();
mBookMarkService = new BookMarkService();
mChapterService = ChapterService.getInstance();
mBookMarkService = BookMarkService.getInstance();
}
public static BookService getInstance() {
if (sInstance == null){
synchronized (BookService.class){
if (sInstance == null){
sInstance = new BookService();
}
}
}
return sInstance;
}
private List<Book> findBooks(String sql, String[] selectionArgs) {
@ -118,6 +129,25 @@ public class BookService extends BaseService {
return book;
}
/**
* 通过路径查书籍本地书籍
* @param path
* @return
*/
public Book findBookByPath(String path){
Book book = null;
try {
Cursor cursor = selectBySql("select id from book where CHAPTER_URL = ?",new String[]{path});
if (cursor.moveToNext()){
String id = cursor.getString(0);
book = getBookById(id);
}
} catch (Exception e) {
e.printStackTrace();
}
return book;
}
/**
* 删除书
* @param id

@ -17,7 +17,18 @@ import java.util.List;
public class ChapterService extends BaseService {
private static volatile ChapterService sInstance;
public static ChapterService getInstance() {
if (sInstance == null){
synchronized (ChapterService.class){
if (sInstance == null){
sInstance = new ChapterService();
}
}
}
return sInstance;
}
private List<Chapter> findChapters(String sql, String[] selectionArgs) {
ArrayList<Chapter> chapters = new ArrayList<>();
try {

@ -15,7 +15,17 @@ import java.util.Date;
public class SearchHistoryService extends BaseService {
private static volatile SearchHistoryService sInstance;
public static SearchHistoryService getInstance() {
if (sInstance == null){
synchronized (SearchHistoryService.class){
if (sInstance == null){
sInstance = new SearchHistoryService();
}
}
}
return sInstance;
}
private ArrayList<SearchHistory> findSearchHistorys(String sql, String[] selectionArgs) {
ArrayList<SearchHistory> searchHistories = new ArrayList<>();
try {

@ -69,7 +69,7 @@ public class BookInfoPresenter implements BasePresenter {
public BookInfoPresenter(BookInfoActivity bookInfoActivity) {
mBookInfoActivity = bookInfoActivity;
mBookService = new BookService();
mBookService = BookService.getInstance();
}
@Override

@ -0,0 +1,87 @@
package xyz.fycz.myreader.ui.filesys;
import xyz.fycz.myreader.base.BaseFragment;
import java.io.File;
import java.util.List;
/**
* @author fengyue
* @date 2020/8/12 20:02
* FileSystemActivity的基础Fragment类
*/
public abstract class BaseFileFragment extends BaseFragment {
protected FileSystemAdapter mAdapter;
protected OnFileCheckedListener mListener;
protected boolean isCheckedAll;
//设置当前列表为全选
public void setCheckedAll(boolean checkedAll){
if (mAdapter == null) return;
isCheckedAll = checkedAll;
mAdapter.setCheckedAll(checkedAll);
}
public void setChecked(boolean checked){
isCheckedAll = checked;
}
//当前fragment是否全选
public boolean isCheckedAll() {
return isCheckedAll;
}
//获取被选中的数量
public int getCheckedCount(){
if (mAdapter == null) return 0;
return mAdapter.getCheckedCount();
}
//获取被选中的文件列表
public List<File> getCheckedFiles(){
return mAdapter != null ? mAdapter.getCheckedFiles() : null;
}
//获取文件的总数
public int getFileCount(){
return mAdapter != null ? mAdapter.getItemCount() : null;
}
//获取可点击的文件的数量
public int getCheckableCount(){
if (mAdapter == null) return 0;
return mAdapter.getCheckableCount();
}
/**
* 删除选中的文件
*/
public void deleteCheckedFiles(){
//删除选中的文件
List<File> files = getCheckedFiles();
//删除显示的文件列表
mAdapter.removeItems(files);
//删除选中的文件
for (File file : files){
if (file.exists()){
file.delete();
}
}
}
//设置文件点击监听事件
public void setOnFileCheckedListener(OnFileCheckedListener listener){
mListener = listener;
}
//文件点击监听
public interface OnFileCheckedListener {
void onItemCheckedChange(boolean isChecked);
void onCategoryChanged();
}
}

@ -0,0 +1,176 @@
package xyz.fycz.myreader.ui.filesys;
import android.os.Bundle;
import android.os.Environment;
import android.widget.TextView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.util.FileStack;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.widget.DividerItemDecoration;
import java.io.File;
import java.io.FileFilter;
import java.util.*;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public class FileCategoryFragment extends BaseFileFragment {
private static final String TAG = "FileCategoryFragment";
@BindView(R.id.file_category_tv_path)
TextView mTvPath;
@BindView(R.id.file_category_tv_back_last)
TextView mTvBackLast;
@BindView(R.id.file_category_rv_content)
RecyclerView mRvContent;
private FileStack mFileStack;
@Override
protected int getContentId() {
return R.layout.fragment_file_category;
}
@Override
protected void initWidget(Bundle savedInstanceState) {
super.initWidget(savedInstanceState);
mFileStack = new FileStack();
setUpAdapter();
}
private void setUpAdapter(){
mAdapter = new FileSystemAdapter();
mRvContent.setLayoutManager(new LinearLayoutManager(getContext()));
mRvContent.addItemDecoration(new DividerItemDecoration(getContext()));
mRvContent.setAdapter(mAdapter);
}
@Override
protected void initClick() {
super.initClick();
mAdapter.setOnItemClickListener(
(view, pos) -> {
File file = mAdapter.getItem(pos);
if (file.isDirectory()){
//保存当前信息。
FileStack.FileSnapshot snapshot = new FileStack.FileSnapshot();
snapshot.filePath = mTvPath.getText().toString();
snapshot.files = new ArrayList<>(mAdapter.getItems());
snapshot.scrollOffset = mRvContent.computeVerticalScrollOffset();
mFileStack.push(snapshot);
//切换下一个文件
toggleFileTree(file);
}
else {
//如果是已加载的文件,则点击事件无效。
String path = mAdapter.getItem(pos).getAbsolutePath();
if (BookService.getInstance().findBookByPath(path) != null){
return;
}
//点击选中
mAdapter.setCheckedItem(pos);
//反馈
if (mListener != null){
mListener.onItemCheckedChange(mAdapter.getItemIsChecked(pos));
}
}
}
);
mTvBackLast.setOnClickListener(
(v) -> {
FileStack.FileSnapshot snapshot = mFileStack.pop();
int oldScrollOffset = mRvContent.computeHorizontalScrollOffset();
if (snapshot == null) return;
mTvPath.setText(snapshot.filePath);
mAdapter.refreshItems(snapshot.files);
mRvContent.scrollBy(0,snapshot.scrollOffset - oldScrollOffset);
//反馈
if (mListener != null){
mListener.onCategoryChanged();
}
}
);
}
@Override
protected void processLogic() {
super.processLogic();
File root = Environment.getExternalStorageDirectory();
toggleFileTree(root);
}
private void toggleFileTree(File file){
//路径名
mTvPath.setText(getString(R.string.file_path,file.getPath()));
//获取数据
File[] files = file.listFiles(new SimpleFileFilter());
//转换成List
List<File> rootFiles = Arrays.asList(files);
//排序
Collections.sort(rootFiles, new FileComparator());
//加入
mAdapter.refreshItems(rootFiles);
//反馈
if (mListener != null){
mListener.onCategoryChanged();
}
}
@Override
public int getFileCount(){
int count = 0;
Set<Map.Entry<File, Boolean>> entrys = mAdapter.getCheckMap().entrySet();
for (Map.Entry<File, Boolean> entry:entrys){
if (!entry.getKey().isDirectory()){
++count;
}
}
return count;
}
public static class FileComparator implements Comparator<File> {
@Override
public int compare(File o1, File o2){
if (o1.isDirectory() && o2.isFile()) {
return -1;
}
if (o2.isDirectory() && o1.isFile()) {
return 1;
}
return o1.getName().compareToIgnoreCase(o2.getName());
}
}
public static class SimpleFileFilter implements FileFilter {
@Override
public boolean accept(File pathname) {
if (pathname.getName().startsWith(".")){
return false;
}
//文件夹内部数量为0
if (pathname.isDirectory() && (pathname.list() == null || pathname.list().length == 0)) {
return false;
}
/**
* 现在只支持TXT文件的显示
*/
//文件内容为空,或者不以txt为开头
if (!pathname.isDirectory() &&
(pathname.length() == 0 || !pathname.getName().endsWith(FileUtils.SUFFIX_TXT))){
return false;
}
return true;
}
}
}

@ -0,0 +1,103 @@
package xyz.fycz.myreader.ui.filesys;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.ViewHolderImpl;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
import java.io.File;
import java.util.HashMap;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public class FileHolder extends ViewHolderImpl<File> {
private ImageView mIvIcon;
private CheckBox mCbSelect;
private TextView mTvName;
private LinearLayout mLlBrief;
private TextView mTvTag;
private TextView mTvSize;
private TextView mTvDate;
private TextView mTvSubCount;
private HashMap<File, Boolean> mSelectedMap;
public FileHolder(HashMap<File, Boolean> selectedMap){
mSelectedMap = selectedMap;
}
@Override
public void initView() {
mIvIcon = findById(R.id.file_iv_icon);
mCbSelect = findById(R.id.file_cb_select);
mTvName = findById(R.id.file_tv_name);
mLlBrief = findById(R.id.file_ll_brief);
mTvTag = findById(R.id.file_tv_tag);
mTvSize = findById(R.id.file_tv_size);
mTvDate = findById(R.id.file_tv_date);
mTvSubCount = findById(R.id.file_tv_sub_count);
}
@Override
public void onBind(File data, int pos) {
//判断是文件还是文件夹
if (data.isDirectory()){
setFolder(data);
}
else {
setFile(data);
}
}
private void setFile(File file){
//选择
if (BookService.getInstance().findBookByPath(file.getAbsolutePath()) != null){
mIvIcon.setImageResource(R.drawable.ic_file_loaded);
mIvIcon.setVisibility(View.VISIBLE);
mCbSelect.setVisibility(View.GONE);
}
else {
boolean isSelected = mSelectedMap.get(file);
mCbSelect.setChecked(isSelected);
mIvIcon.setVisibility(View.GONE);
mCbSelect.setVisibility(View.VISIBLE);
}
mLlBrief.setVisibility(View.VISIBLE);
mTvSubCount.setVisibility(View.GONE);
mTvName.setText(file.getName());
mTvSize.setText(FileUtils.getFileSize(file.length()));
mTvDate.setText(StringUtils.dateConvert(file.lastModified(), APPCONST.FORMAT_FILE_DATE));
}
public void setFolder(File folder){
//图片
mIvIcon.setVisibility(View.VISIBLE);
mCbSelect.setVisibility(View.GONE);
mIvIcon.setImageResource(R.drawable.ic_folder);
//名字
mTvName.setText(folder.getName());
//介绍
mLlBrief.setVisibility(View.GONE);
mTvSubCount.setVisibility(View.VISIBLE);
mTvSubCount.setText(getContext().getString(R.string.file_sub_count, folder.list().length));
}
@Override
protected int getItemLayoutId() {
return R.layout.item_file;
}
}

@ -0,0 +1,266 @@
package xyz.fycz.myreader.ui.filesys;
import android.widget.Button;
import android.widget.CheckBox;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import butterknife.BindView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.BaseTabActivity;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.util.TextHelper;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public class FileSystemActivity extends BaseTabActivity {
private static final String TAG = "FileSystemActivity";
@BindView(R.id.file_system_cb_selected_all)
CheckBox mCbSelectAll;
@BindView(R.id.file_system_btn_delete)
Button mBtnDelete;
@BindView(R.id.file_system_btn_add_book)
Button mBtnAddBook;
private LocalBookFragment mLocalFragment;
private FileCategoryFragment mCategoryFragment;
private BaseFileFragment mCurFragment;
private BaseFileFragment.OnFileCheckedListener mListener = new BaseFileFragment.OnFileCheckedListener() {
@Override
public void onItemCheckedChange(boolean isChecked) {
changeMenuStatus();
}
@Override
public void onCategoryChanged() {
//状态归零
mCurFragment.setCheckedAll(false);
//改变菜单
changeMenuStatus();
//改变是否能够全选
changeCheckedAllStatus();
}
};
@Override
protected List<Fragment> createTabFragments() {
mLocalFragment = new LocalBookFragment();
mCategoryFragment = new FileCategoryFragment();
return Arrays.asList(mLocalFragment,mCategoryFragment);
}
@Override
protected List<String> createTabTitles() {
return Arrays.asList("智能导入","手机目录");
}
@Override
protected int getContentId() {
return R.layout.activity_file_system;
}
@Override
protected void setUpToolbar(Toolbar toolbar) {
super.setUpToolbar(toolbar);
setStatusBarColor(R.color.sys_line);
getSupportActionBar().setTitle("添加本地");
}
@Override
protected void initClick() {
super.initClick();
mCbSelectAll.setOnClickListener(
(view) -> {
//设置全选状态
boolean isChecked = mCbSelectAll.isChecked();
mCurFragment.setCheckedAll(isChecked);
//改变菜单状态
changeMenuStatus();
}
);
mVp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if (position == 0){
mCurFragment = mLocalFragment;
}
else {
mCurFragment = mCategoryFragment;
}
//改变菜单状态
changeMenuStatus();
//改变是否能够全选
changeCheckedAllStatus();
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
mBtnAddBook.setOnClickListener(
(v) -> {
//获取选中的文件
List<File> files = mCurFragment.getCheckedFiles();
//转换成Book,并存储
List<Book> books = convertBook(files);
BookService.getInstance()
.addBooks(books);
//设置HashMap为false
mCurFragment.setCheckedAll(false);
//改变菜单状态
changeMenuStatus();
//改变是否可以全选
changeCheckedAllStatus();
//提示加入书架成功
TextHelper.showText(getResources().getString(R.string.file_add_succeed, books.size()));
}
);
mBtnDelete.setOnClickListener(
(v) -> {
//弹出,确定删除文件吗。
DialogCreator.createCommonDialog(this, "删除文件", "确定删除文件吗?",
true, (dialog, which) -> {
//删除选中的文件
mCurFragment.deleteCheckedFiles();
//提示删除文件成功
TextHelper.showText("删除文件成功");
}, null);
}
);
mLocalFragment.setOnFileCheckedListener(mListener);
mCategoryFragment.setOnFileCheckedListener(mListener);
}
@Override
protected void processLogic() {
super.processLogic();
mCurFragment = mLocalFragment;
}
/**
* 将文件转换成CollBook
* @param files:需要加载的文件列表
* @return
*/
private List<Book> convertBook(List<File> files){
List<Book> books = new ArrayList<>(files.size());
for(File file : files){
//判断文件是否存在
if (!file.exists()) continue;
Book book = new Book();
book.setName(file.getName().replace(".txt", ""));
book.setChapterUrl(file.getAbsolutePath());
book.setType("本地书籍");
book.setHistoryChapterId("未开始阅读");
book.setNewestChapterTitle("未拆分章节");
book.setAuthor("本地书籍");
book.setSource(BookSource.local.toString());
book.setDesc("");
books.add(book);
}
return books;
}
/**
* 改变底部选择栏的状态
*/
private void changeMenuStatus(){
//点击、删除状态的设置
if (mCurFragment.getCheckedCount() == 0){
mBtnAddBook.setText(getString(R.string.file_add_shelf));
//设置某些按钮的是否可点击
setMenuClickable(false);
if (mCbSelectAll.isChecked()){
mCurFragment.setChecked(false);
mCbSelectAll.setChecked(mCurFragment.isCheckedAll());
}
}
else {
mBtnAddBook.setText(getString(R.string.file_add_shelves, mCurFragment.getCheckedCount()));
setMenuClickable(true);
//全选状态的设置
//如果选中的全部的数据,则判断为全选
if (mCurFragment.getCheckedCount() == mCurFragment.getCheckableCount()){
//设置为全选
mCurFragment.setChecked(true);
mCbSelectAll.setChecked(mCurFragment.isCheckedAll());
}
//如果曾今是全选则替换
else if (mCurFragment.isCheckedAll()){
mCurFragment.setChecked(false);
mCbSelectAll.setChecked(mCurFragment.isCheckedAll());
}
}
//重置全选的文字
if (mCurFragment.isCheckedAll()){
mCbSelectAll.setText("取消");
}
else {
mCbSelectAll.setText("全选");
}
}
private void setMenuClickable(boolean isClickable){
//设置是否可删除
mBtnDelete.setEnabled(isClickable);
mBtnDelete.setClickable(isClickable);
//设置是否可添加书籍
mBtnAddBook.setEnabled(isClickable);
mBtnAddBook.setClickable(isClickable);
}
/**
* 改变全选按钮的状态
*/
private void changeCheckedAllStatus(){
//获取可选择的文件数量
int count = mCurFragment.getCheckableCount();
//设置是否能够全选
if (count > 0){
mCbSelectAll.setClickable(true);
mCbSelectAll.setEnabled(true);
}
else {
mCbSelectAll.setClickable(false);
mCbSelectAll.setEnabled(false);
}
}
}

@ -0,0 +1,148 @@
package xyz.fycz.myreader.ui.filesys;
import xyz.fycz.myreader.base.BaseListAdapter;
import xyz.fycz.myreader.base.IViewHolder;
import xyz.fycz.myreader.greendao.service.BookService;
import java.io.File;
import java.util.*;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public class FileSystemAdapter extends BaseListAdapter<File> {
//记录item是否被选中的Map
private HashMap<File, Boolean> mCheckMap = new HashMap<>();
private int mCheckedCount = 0;
private BookService mBookService = new BookService();
@Override
protected IViewHolder<File> createViewHolder(int viewType) {
return new FileHolder(mCheckMap);
}
@Override
public void refreshItems(List<File> list) {
mCheckMap.clear();
for(File file : list){
mCheckMap.put(file, false);
}
super.refreshItems(list);
}
@Override
public void addItem(File value) {
mCheckMap.put(value, false);
super.addItem(value);
}
@Override
public void addItem(int index, File value) {
mCheckMap.put(value, false);
super.addItem(index, value);
}
@Override
public void addItems(List<File> values) {
for(File file : values){
mCheckMap.put(file, false);
}
super.addItems(values);
}
@Override
public void removeItem(File value) {
mCheckMap.remove(value);
super.removeItem(value);
}
@Override
public void removeItems(List<File> value) {
//删除在HashMap中的文件
for (File file : value){
mCheckMap.remove(file);
//因为,能够被移除的文件,肯定是选中的
--mCheckedCount;
}
//删除列表中的文件
super.removeItems(value);
}
//设置点击切换
public void setCheckedItem(int pos){
File file = getItem(pos);
if (isFileLoaded(file.getAbsolutePath())) return;
boolean isSelected = mCheckMap.get(file);
if (isSelected){
mCheckMap.put(file, false);
--mCheckedCount;
}
else{
mCheckMap.put(file, true);
++mCheckedCount;
}
notifyDataSetChanged();
}
public void setCheckedAll(boolean isChecked){
Set<Map.Entry<File, Boolean>> entrys = mCheckMap.entrySet();
mCheckedCount = 0;
for (Map.Entry<File, Boolean> entry:entrys){
//必须是文件,必须没有被收藏
if (entry.getKey().isFile() && !isFileLoaded(entry.getKey().getAbsolutePath())){
entry.setValue(isChecked);
//如果选中,则增加点击的数量
if (isChecked){
++mCheckedCount;
}
}
}
notifyDataSetChanged();
}
private boolean isFileLoaded(String path){
//如果是已加载的文件,则点击事件无效。
if (mBookService.findBookByPath(path) != null){
return true;
}
return false;
}
public int getCheckableCount(){
List<File> files = getItems();
int count = 0;
for (File file : files){
if (!isFileLoaded(file.getAbsolutePath()) && file.isFile())
++count;
}
return count;
}
public boolean getItemIsChecked(int pos){
File file = getItem(pos);
return mCheckMap.get(file);
}
public List<File> getCheckedFiles(){
List<File> files = new ArrayList<>();
Set<Map.Entry<File, Boolean>> entrys = mCheckMap.entrySet();
for (Map.Entry<File, Boolean> entry:entrys){
if (entry.getValue()){
files.add(entry.getKey());
}
}
return files;
}
public int getCheckedCount(){
return mCheckedCount;
}
public HashMap<File, Boolean> getCheckMap(){
return mCheckMap;
}
}

@ -0,0 +1,89 @@
package xyz.fycz.myreader.ui.filesys;
import android.media.MediaScannerConnection;
import android.os.Bundle;
import android.os.Environment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.util.media.MediaStoreHelper;
import xyz.fycz.myreader.widget.DividerItemDecoration;
import xyz.fycz.myreader.widget.RefreshLayout;
/**
* @author fengyue
* @date 2020/8/12 20:02
* 本地书籍
*/
public class LocalBookFragment extends BaseFileFragment {
@BindView(R.id.refresh_layout)
RefreshLayout mRlRefresh;
@BindView(R.id.local_book_rv_content)
RecyclerView mRvContent;
@Override
protected int getContentId() {
return R.layout.fragment_local_book;
}
@Override
protected void initWidget(Bundle savedInstanceState) {
super.initWidget(savedInstanceState);
setUpAdapter();
}
private void setUpAdapter() {
mAdapter = new FileSystemAdapter();
mRvContent.setLayoutManager(new LinearLayoutManager(getContext()));
mRvContent.addItemDecoration(new DividerItemDecoration(getContext()));
mRvContent.setAdapter(mAdapter);
}
@Override
protected void initClick() {
super.initClick();
mAdapter.setOnItemClickListener(
(view, pos) -> {
//如果是已加载的文件,则点击事件无效。
String path = mAdapter.getItem(pos).getAbsolutePath();
if (BookService.getInstance().findBookByPath(path) != null) {
return;
}
//点击选中
mAdapter.setCheckedItem(pos);
//反馈
if (mListener != null) {
mListener.onItemCheckedChange(mAdapter.getItemIsChecked(pos));
}
}
);
}
@Override
protected void processLogic() {
super.processLogic();
//更新媒体库
MediaScannerConnection.scanFile(getContext(), new String[]{Environment
.getExternalStorageDirectory().getAbsolutePath()}, new String[]{"text/plain"}, null);
MediaStoreHelper.getAllBookFile(getActivity(),
(files) -> {
if (files.isEmpty()) {
mRlRefresh.showEmpty();
} else {
mAdapter.refreshItems(files);
mRlRefresh.showFinish();
//反馈
if (mListener != null) {
mListener.onCategoryChanged();
}
}
});
}
}

@ -1,5 +1,6 @@
package xyz.fycz.myreader.ui.font;
import android.annotation.SuppressLint;
import android.os.Handler;
import android.os.Message;
import android.view.View;
@ -8,6 +9,7 @@ import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.BasePresenter;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.enums.Font;
import xyz.fycz.myreader.util.IOUtils;
import xyz.fycz.myreader.util.TextHelper;
import xyz.fycz.myreader.util.utils.FileUtils;
@ -25,6 +27,7 @@ public class FontsPresenter implements BasePresenter {
private FontsAdapter mFontsAdapter;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
@ -107,20 +110,7 @@ public class FontsPresenter implements BasePresenter {
e.printStackTrace();
TextHelper.showText("读取字体文件出错!\n" + e.getLocalizedMessage());
}finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
IOUtils.close(fis, fos);
}
}
}

@ -4,10 +4,12 @@ package xyz.fycz.myreader.ui.home;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.content.FileProvider;
import android.widget.ImageView;
@ -22,6 +24,7 @@ import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.custom.CircleImageView;
import xyz.fycz.myreader.ui.filesys.FileSystemActivity;
import xyz.fycz.myreader.util.TextHelper;
import butterknife.BindView;
@ -103,6 +106,26 @@ public class MainActivity extends BaseActivity {
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case APPCONST.PERMISSIONS_REQUEST_STORAGE: {
// 如果取消权限,则返回的值为0
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//跳转到 FileSystemActivity
Intent intent = new Intent(this, FileSystemActivity.class);
startActivity(intent);
} else {
TextHelper.showText("用户拒绝开启读写权限");
}
return;
}
}
}
@Override
protected void onDestroy() {
MyApplication.getApplication().shutdownThreadPool();

@ -131,6 +131,9 @@ public class MainPresenter implements BasePresenter {
* @return
*/
public boolean ismEditState(){
if (mBookcaseFragment.getmBookcasePresenter() == null) {
return false;
}
return mBookcaseFragment.getmBookcasePresenter().ismEditState();
}
}

@ -60,8 +60,8 @@ public abstract class BookcaseAdapter extends DragAdapter {
mResourceId = textViewResourceId;
list = objects;
mEditState = editState;
mBookService = new BookService();
mChapterService = new ChapterService();
mBookService = BookService.getInstance();
mChapterService = ChapterService.getInstance();
mBookcasePresenter = bookcasePresenter;
}

@ -80,12 +80,7 @@ public class BookcaseDetailedAdapter extends BookcaseAdapter {
viewHolder.tvBookName.setText(book.getName());
viewHolder.tvBookAuthor.setText(book.getAuthor());
viewHolder.ivDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showDeleteBookDialog(book);
}
});
viewHolder.ivDelete.setOnClickListener(v -> showDeleteBookDialog(book));
viewHolder.tvHistoryChapter.setText(book.getHistoryChapterId());
assert book.getNewestChapterTitle() != null;
viewHolder.tvNewestChapter.setText(book.getNewestChapterTitle().replace("最近更新 ", ""));

@ -1,5 +1,6 @@
package xyz.fycz.myreader.ui.home.bookcase;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.DialogInterface;
import android.content.Intent;
@ -14,6 +15,7 @@ import android.widget.ArrayAdapter;
import android.widget.PopupMenu;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
@ -51,6 +53,7 @@ import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.greendao.service.ChapterService;
import xyz.fycz.myreader.ui.about.AboutActivity;
import xyz.fycz.myreader.ui.filesys.FileSystemActivity;
import xyz.fycz.myreader.ui.home.MainActivity;
import xyz.fycz.myreader.ui.search.SearchBookActivity;
import xyz.fycz.myreader.ui.user.LoginActivity;
@ -69,6 +72,7 @@ public class BookcasePresenter implements BasePresenter {
private final BookService mBookService;
private final ChapterService mChapterService;
private final MainActivity mMainActivity;
private PermissionsChecker mPermissionsChecker;
private boolean isBookcaseStyleChange;
private Setting mSetting;
private final List<Book> errorLoadingBooks = new ArrayList<>();
@ -96,6 +100,11 @@ public class BookcasePresenter implements BasePresenter {
MyApplication.getmContext().getString(R.string.menu_backup_webRestore),
MyApplication.getmContext().getString(R.string.menu_backup_autoSyn)
};
static final String[] PERMISSIONS = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
// private ChapterService mChapterService;
@SuppressLint("HandlerLeak")
public final Handler mHandler = new Handler() {
@ -152,8 +161,9 @@ public class BookcasePresenter implements BasePresenter {
public BookcasePresenter(BookcaseFragment bookcaseFragment) {
mBookcaseFragment = bookcaseFragment;
mBookService = new BookService();
mChapterService = new ChapterService();
mBookService = BookService.getInstance();
;
mChapterService = ChapterService.getInstance();
mMainActivity = ((MainActivity) (mBookcaseFragment.getActivity()));
// mChapterService = new ChapterService();
mSetting = SysManager.getSetting();
@ -437,17 +447,32 @@ public class BookcasePresenter implements BasePresenter {
init();
return true;
case R.id.action_addLocalBook:
TextHelper.showText("请选择一个txt格式的书籍文件");
/*TextHelper.showText("请选择一个txt格式的书籍文件");
Intent addIntent = new Intent(Intent.ACTION_GET_CONTENT);
addIntent.setType("text/plain");
addIntent.addCategory(Intent.CATEGORY_OPENABLE);
mMainActivity.startActivityForResult(addIntent, APPCONST.SELECT_FILE_CODE);
mMainActivity.startActivityForResult(addIntent, APPCONST.SELECT_FILE_CODE);*/
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
if (mPermissionsChecker == null) {
mPermissionsChecker = new PermissionsChecker(mMainActivity);
}
//获取读取和写入SD卡的权限
if (mPermissionsChecker.lacksPermissions(PERMISSIONS)) {
//请求权限
ActivityCompat.requestPermissions(mMainActivity, PERMISSIONS, APPCONST.PERMISSIONS_REQUEST_STORAGE);
return true;
}
}
Intent intent = new Intent(mMainActivity, FileSystemActivity.class);
mMainActivity.startActivity(intent);
break;
case R.id.action_syn:
if (!UserService.isLogin()){
if (!UserService.isLogin()) {
TextHelper.showText("请先登录!");
Intent intent = new Intent(mMainActivity, LoginActivity.class);
mMainActivity.startActivity(intent);
Intent loginIntent = new Intent(mMainActivity, LoginActivity.class);
mMainActivity.startActivity(loginIntent);
return true;
}
if (mSetting.isAutoSyn()) {
@ -578,7 +603,7 @@ public class BookcasePresenter implements BasePresenter {
* 恢复
*/
private void webRestore() {
if (!NetworkUtils.isNetWorkAvailable()){
if (!NetworkUtils.isNetWorkAvailable()) {
TextHelper.showText("无网络连接!");
return;
}
@ -770,11 +795,11 @@ public class BookcasePresenter implements BasePresenter {
* 同步书架
*/
private void synBookcaseToWeb(boolean isAutoSyn) {
if (!NetworkUtils.isNetWorkAvailable()){
if (!NetworkUtils.isNetWorkAvailable()) {
if (!isAutoSyn) {
TextHelper.showText("无网络连接!");
}
return;
return;
}
Date nowTime = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd");

@ -181,9 +181,9 @@ public class ReadPresenter implements BasePresenter {
public ReadPresenter(ReadActivity readActivity) {
mReadActivity = readActivity;
mBookService = new BookService();
mChapterService = new ChapterService();
mBookMarkService = new BookMarkService();
mBookService = BookService.getInstance();;
mChapterService = ChapterService.getInstance();
mBookMarkService = BookMarkService.getInstance();
mSetting = SysManager.getSetting();
}

@ -26,7 +26,6 @@ public class CatalogActivityPresenter implements BasePresenter {
private CatalogActivity mCatalogActivity;
private ArrayList<Fragment> mFragments = new ArrayList<>();
private String[] tabTitle = {"目录", "书签"};
private Setting mSetting;
private boolean isDayStyle;
private FragmentPagerAdapter tabAdapter;
@ -42,7 +41,7 @@ public class CatalogActivityPresenter implements BasePresenter {
if (!isDayStyle){
mCatalogActivity.setStatusBar(R.color.sys_dialog_setting_bg);
mCatalogActivity.getRlCommonTitle().setBackground(mCatalogActivity.getDrawable(R.color.sys_dialog_setting_bg));
mCatalogActivity.getTvBack().setImageDrawable(mCatalogActivity.getDrawable(R.mipmap.larrow3));
mCatalogActivity.getTvBack().setImageDrawable(mCatalogActivity.getDrawable(R.mipmap.larrow_white2));
mCatalogActivity.getIvCancel().setImageDrawable(mCatalogActivity.getDrawable(R.mipmap.ic_cha_black));
mCatalogActivity.getIvSearch().setImageDrawable(mCatalogActivity.getDrawable(R.mipmap.b9));
mCatalogActivity.getEtSearch().setTextColor(mCatalogActivity.getResources().getColor(R.color.sys_night_word));
@ -78,7 +77,9 @@ public class CatalogActivityPresenter implements BasePresenter {
mCatalogActivity.getIvSearch().setVisibility(View.VISIBLE);
mCatalogActivity.getEtSearch().setVisibility(View.GONE);
mCatalogActivity.getIvCancel().setVisibility(View.GONE);
mCatalogActivity.getEtSearch().setText("");
if(!"".equals(mCatalogActivity.getEtSearch().getText().toString())){
mCatalogActivity.getEtSearch().setText("");
}
});
mCatalogActivity.getEtSearch().addTextChangedListener(new TextWatcher() {

@ -31,7 +31,7 @@ public class CatalogPresenter implements BasePresenter {
public CatalogPresenter(CatalogFragment mCatalogFragment) {
this.mCatalogFragment = mCatalogFragment;
mChapterService = new ChapterService();
mChapterService = ChapterService.getInstance();
}
@Override

@ -92,7 +92,7 @@ public class SearchBookPrensenter implements BasePresenter {
public SearchBookPrensenter(SearchBookActivity searchBookActivity) {
mSearchBookActivity = searchBookActivity;
mSearchHistoryService = new SearchHistoryService();
mSearchHistoryService = SearchHistoryService.getInstance();
for (int i = 0; i < suggestion.length; i++) {
mSuggestions.add(suggestion[i]);
}

@ -15,6 +15,7 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.*;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.google.android.material.textfield.TextInputLayout;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.util.utils.StringUtils;
@ -27,15 +28,15 @@ public class LoginActivity extends BaseActivity implements TextWatcher {
@BindView(R.id.tv_title_text)
TextView tvTitleText;
@BindView(R.id.et_user)
EditText user;
TextInputLayout user;
@BindView(R.id.et_password)
EditText password;
TextInputLayout password;
@BindView(R.id.bt_login)
Button loginBtn;
@BindView(R.id.tv_register)
TextView tvRegister;
@BindView(R.id.et_captcha)
EditText etCaptcha;
TextInputLayout etCaptcha;
@BindView(R.id.iv_captcha)
ImageView ivCaptcha;
private LoginPresenter mLoginPresenter;
@ -98,9 +99,9 @@ public class LoginActivity extends BaseActivity implements TextWatcher {
//禁止输入中文
StringUtils.isNotChinese(s);
//判断两个输入框是否有内容
if (user.getText().toString().length() > 3 &&
password.getText().toString().length() > 5 &&
etCaptcha.getText().toString().length() > 0){
if (user.getEditText().getText().toString().length() > 0 &&
password.getEditText().getText().toString().length() > 0 &&
etCaptcha.getEditText().getText().toString().length() > 0){
//按钮可以点击
loginBtn.setEnabled(true);
}else{
@ -111,11 +112,11 @@ public class LoginActivity extends BaseActivity implements TextWatcher {
public EditText getUser() {
return user;
return user.getEditText();
}
public EditText getPassword() {
return password;
return password.getEditText();
}
public Button getLoginBtn() {
@ -135,7 +136,7 @@ public class LoginActivity extends BaseActivity implements TextWatcher {
}
public EditText getEtCaptcha() {
return etCaptcha;
return etCaptcha.getEditText();
}
public ImageView getIvCaptcha() {

@ -1,6 +1,7 @@
package xyz.fycz.myreader.ui.user;
import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Handler;
@ -69,50 +70,51 @@ public class LoginPresenter implements BasePresenter {
}
});
mLoginActivity.getLoginBtn().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mHandler.sendMessage(mHandler.obtainMessage(2));
if (!code.toLowerCase().equals(mLoginActivity.getEtCaptcha().getText().toString().toLowerCase())){
DialogCreator.createTipDialog(mLoginActivity, "验证码错误!");
return;
}
if (!NetworkUtils.isNetWorkAvailable()) {
TextHelper.showText("无网络连接!");
return;
}
mLoginActivity.getLoginBtn().setEnabled(false);
final String loginName = mLoginActivity.getUser().getText().toString().trim();
String loginPwd = mLoginActivity.getPassword().getText().toString();
final Map<String, String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName", loginName);
userLoginInfo.put("loginPwd", CyptoUtils.encode(APPCONST.KEY, loginPwd));
//验证用户名和密码
UserService.login(userLoginInfo, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
String result = (String) o;
String[] info = result.split(":");
int resultCode = Integer.parseInt(info[0].trim());
String resultName = info[1].trim();
//最后输出结果
if (resultCode == 102) {
UserService.writeConfig(userLoginInfo);
UserService.writeUsername(loginName);
mLoginActivity.finish();
} else {
mHandler.sendMessage(mHandler.obtainMessage(1));
}
TextHelper.showText(resultName);
mLoginActivity.getLoginBtn().setOnClickListener(v -> {
mHandler.sendMessage(mHandler.obtainMessage(2));
if (!code.toLowerCase().equals(mLoginActivity.getEtCaptcha().getText().toString().toLowerCase())){
DialogCreator.createTipDialog(mLoginActivity, "验证码错误!");
return;
}
if (!NetworkUtils.isNetWorkAvailable()) {
TextHelper.showText("无网络连接!");
return;
}
ProgressDialog dialog = DialogCreator.createProgressDialog(mLoginActivity, null, "正在登陆...");
mLoginActivity.getLoginBtn().setEnabled(false);
final String loginName = mLoginActivity.getUser().getText().toString().trim();
String loginPwd = mLoginActivity.getPassword().getText().toString();
final Map<String, String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName", loginName);
userLoginInfo.put("loginPwd", CyptoUtils.encode(APPCONST.KEY, loginPwd));
//验证用户名和密码
UserService.login(userLoginInfo, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
String result = (String) o;
String[] info = result.split(":");
int resultCode = Integer.parseInt(info[0].trim());
String resultName = info[1].trim();
//最后输出结果
if (resultCode == 102) {
UserService.writeConfig(userLoginInfo);
UserService.writeUsername(loginName);
mLoginActivity.finish();
} else {
mHandler.sendMessage(mHandler.obtainMessage(1));
dialog.dismiss();
}
TextHelper.showText(resultName);
}
@Override
public void onError(Exception e) {
TextHelper.showText("登录失败\n" + e.getLocalizedMessage());
}
});
@Override
public void onError(Exception e) {
TextHelper.showText("登录失败\n" + e.getLocalizedMessage());
mHandler.sendMessage(mHandler.obtainMessage(1));
dialog.dismiss();
}
});
}
});
mLoginActivity.getTvRegister().setOnClickListener(new View.OnClickListener() {

@ -15,6 +15,7 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.*;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.google.android.material.textfield.TextInputLayout;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.BaseActivity;
@ -26,13 +27,13 @@ public class RegisterActivity extends BaseActivity{
@BindView(R.id.tv_title_text)
TextView tvTitleText;
@BindView(R.id.et_username)
EditText etUsername;
TextInputLayout etUsername;
@BindView(R.id.et_password)
EditText etPassword;
TextInputLayout etPassword;
@BindView(R.id.et_rp_password)
EditText etRpPassword;
TextInputLayout etRpPassword;
@BindView(R.id.et_captcha)
EditText etCaptcha;
TextInputLayout etCaptcha;
@BindView(R.id.iv_captcha)
ImageView ivCaptcha;
@BindView(R.id.bt_register)
@ -63,19 +64,19 @@ public class RegisterActivity extends BaseActivity{
}
public EditText getEtUsername() {
return etUsername;
return etUsername.getEditText();
}
public EditText getEtPassword() {
return etPassword;
return etPassword.getEditText();
}
public EditText getEtRpPassword() {
return etRpPassword;
return etRpPassword.getEditText();
}
public EditText getEtCaptcha() {
return etCaptcha;
return etCaptcha.getEditText();
}
public ImageView getIvCaptcha() {

@ -1,6 +1,7 @@
package xyz.fycz.myreader.ui.user;
import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
@ -199,6 +200,7 @@ public class RegisterPresenter implements BasePresenter {
}else if(!mRegisterActivity.getCbAgreement().isChecked()){
DialogCreator.createTipDialog(mRegisterActivity, "请勾选同意《用户服务协议》");
}else {
ProgressDialog dialog = DialogCreator.createProgressDialog(mRegisterActivity, null, "正在注册...");
Map<String, String> userRegisterInfo = new HashMap<>();
userRegisterInfo.put("username", username);
userRegisterInfo.put("password", password);
@ -211,11 +213,13 @@ public class RegisterPresenter implements BasePresenter {
UserService.writeUsername(username);
mRegisterActivity.finish();
}
dialog.dismiss();
TextHelper.showText(info[1]);
}
@Override
public void onError(Exception e) {
TextHelper.showText("注册失败:\n" + e.getLocalizedMessage());
dialog.dismiss();
}
});
}

@ -0,0 +1,49 @@
package xyz.fycz.myreader.util;
import java.io.File;
import java.util.List;
/**
* Created by newbiechen on 17-5-28.
*/
public class FileStack {
private Node node = null;
private int count = 0;
public void push(FileSnapshot fileSnapshot){
if (fileSnapshot == null) return;
Node fileNode = new Node();
fileNode.fileSnapshot = fileSnapshot;
fileNode.next = node;
node = fileNode;
++count;
}
public FileSnapshot pop(){
Node fileNode = node;
if (fileNode == null) return null;
FileSnapshot fileSnapshot = fileNode.fileSnapshot;
node = fileNode.next;
--count;
return fileSnapshot;
}
public int getSize(){
return count;
}
//节点
public class Node {
FileSnapshot fileSnapshot;
Node next;
}
//文件快照
public static class FileSnapshot{
public String filePath;
public List<File> files;
public int scrollOffset;
}
}

@ -0,0 +1,272 @@
package xyz.fycz.myreader.util;
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.callback.ResultCallback;
import static xyz.fycz.myreader.util.DipPxUtil.dp2px;
/**
* @author fengyue
* @date 2020/8/13 7:56
*/
public class ImageUtil {
/**
* 设置水印图片在左上角
* @param context
* @param src
* @param watermark
* @param paddingLeft
* @param paddingTop
* @return
*/
public static Bitmap createWaterMaskLeftTop(
Context context, Bitmap src, Bitmap watermark,
int paddingLeft, int paddingTop) {
return createWaterMaskBitmap(src, watermark,
dp2px(context, paddingLeft), dp2px(context, paddingTop));
}
private static Bitmap createWaterMaskBitmap(Bitmap src, Bitmap watermark,
int paddingLeft, int paddingTop) {
if (src == null) {
return null;
}
int width = src.getWidth();
int height = src.getHeight();
//创建一个bitmap
Bitmap newb = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图
//将该图片作为画布
Canvas canvas = new Canvas(newb);
//在画布 0,0坐标上开始绘制原始图片
canvas.drawBitmap(src, 0, 0, null);
//在画布上绘制水印图片
canvas.drawBitmap(watermark, paddingLeft, paddingTop, null);
// 保存
canvas.save();
// 存储
canvas.restore();
return newb;
}
/**
* 设置水印图片在右下角
* @param context
* @param src
* @param watermark
* @param paddingRight
* @param paddingBottom
* @return
*/
public static Bitmap createWaterMaskRightBottom(
Context context, Bitmap src, Bitmap watermark,
int paddingRight, int paddingBottom) {
return createWaterMaskBitmap(src, watermark,
src.getWidth() - watermark.getWidth() - dp2px(context, paddingRight),
src.getHeight() - watermark.getHeight() - dp2px(context, paddingBottom));
}
/**
* 设置水印图片到右上角
* @param context
* @param src
* @param watermark
* @param paddingRight
* @param paddingTop
* @return
*/
public static Bitmap createWaterMaskRightTop(
Context context, Bitmap src, Bitmap watermark,
int paddingRight, int paddingTop) {
return createWaterMaskBitmap( src, watermark,
src.getWidth() - watermark.getWidth() - dp2px(context, paddingRight),
dp2px(context, paddingTop));
}
/**
* 设置水印图片到左下角
* @param context
* @param src
* @param watermark
* @param paddingLeft
* @param paddingBottom
* @return
*/
public static Bitmap createWaterMaskLeftBottom(
Context context, Bitmap src, Bitmap watermark,
int paddingLeft, int paddingBottom) {
return createWaterMaskBitmap(src, watermark, dp2px(context, paddingLeft),
src.getHeight() - watermark.getHeight() - dp2px(context, paddingBottom));
}
/**
* 设置水印图片到中间
* @param src
* @param src
* @param watermark
* @return
*/
public static Bitmap createWaterMaskCenter(Bitmap src, Bitmap watermark) {
return createWaterMaskBitmap(src, watermark,
(src.getWidth() - watermark.getWidth()) / 2,
(src.getHeight() - watermark.getHeight()) / 2);
}
/**
* 给图片添加文字到左上角
* @param context
* @param bitmap
* @param text
* @return
*/
public static Bitmap drawTextToLeftTop(Context context, Bitmap bitmap, String text,
int size, int color, int paddingLeft, int paddingTop) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setTextSize(dp2px(context, size));
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
return drawTextToBitmap(context, bitmap, text, paint, bounds,
dp2px(context, paddingLeft),
dp2px(context, paddingTop) + bounds.height());
}
/**
* 绘制文字到右下角
* @param context
* @param bitmap
* @param text
* @param size
* @param color
* @param paddingRight
* @param paddingBottom
* @return
*/
public static Bitmap drawTextToRightBottom(Context context, Bitmap bitmap, String text,
int size, int color, int paddingRight, int paddingBottom) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setTextSize(dp2px(context, size));
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
return drawTextToBitmap(context, bitmap, text, paint, bounds,
bitmap.getWidth() - bounds.width() - dp2px(context, paddingRight),
bitmap.getHeight() - dp2px(context, paddingBottom));
}
/**
* 绘制文字到右上方
* @param context
* @param bitmap
* @param text
* @param size
* @param color
* @param paddingRight
* @param paddingTop
* @return
*/
public static Bitmap drawTextToRightTop(Context context, Bitmap bitmap, String text,
int size, int color, int paddingRight, int paddingTop) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setTextSize(dp2px(context, size));
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
return drawTextToBitmap(context, bitmap, text, paint, bounds,
bitmap.getWidth() - bounds.width() - dp2px(context, paddingRight),
dp2px(context, paddingTop) + bounds.height());
}
/**
* 绘制文字到左下方
* @param context
* @param bitmap
* @param text
* @param size
* @param color
* @param paddingLeft
* @param paddingBottom
* @return
*/
public static Bitmap drawTextToLeftBottom(Context context, Bitmap bitmap, String text,
int size, int color, int paddingLeft, int paddingBottom) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setTextSize(dp2px(context, size));
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
return drawTextToBitmap(context, bitmap, text, paint, bounds,
dp2px(context, paddingLeft),
bitmap.getHeight() - dp2px(context, paddingBottom));
}
/**
* 绘制文字到中间
* @param context
* @param bitmap
* @param text
* @param size
* @param color
* @return
*/
public static Bitmap drawTextToCenter(Context context, Bitmap bitmap, String text,
int size, int color) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setTextSize(dp2px(context, size));
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
return drawTextToBitmap(context, bitmap, text, paint, bounds,
(bitmap.getWidth() - bounds.width()) / 2,
(bitmap.getHeight() + bounds.height()) / 2);
}
//图片上绘制文字
private static Bitmap drawTextToBitmap(Context context, Bitmap bitmap, String text,
Paint paint, Rect bounds, int paddingLeft, int paddingTop) {
android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();
paint.setDither(true); // 获取跟清晰的图像采样
paint.setFilterBitmap(true);// 过滤一些
// paint.setFakeBoldText(true); //加粗
if (bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
canvas.drawText(text, paddingLeft, paddingTop, paint);
return bitmap;
}
/**
* 缩放图片
* @param src
* @param w
* @param h
* @return
*/
public static Bitmap scaleWithWH(Bitmap src, double w, double h) {
if (w == 0 || h == 0 || src == null) {
return src;
} else {
// 记录src的宽高
int width = src.getWidth();
int height = src.getHeight();
// 创建一个matrix容器
Matrix matrix = new Matrix();
// 计算缩放比例
float scaleWidth = (float) (w / width);
float scaleHeight = (float) (h / height);
// 开始缩放
matrix.postScale(scaleWidth, scaleHeight);
// 创建缩放后的图片
return Bitmap.createBitmap(src, 0, 0, width, height, matrix, true);
}
}
}

@ -0,0 +1,71 @@
package xyz.fycz.myreader.util;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import xyz.fycz.myreader.R;
/**
* Created by newbiechen on 17-4-15.
*/
public class StatusBarCompat
{
private static final int INVALID_VAL = -1;
private static final int COLOR_DEFAULT = Color.parseColor("#20000000");
public static void compat(Activity activity, int statusColor)
{
//在SDK21以上,设置StatusBar的Color
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
Window window = activity.getWindow();
//取消设置透明状态栏,使 ContentView 内容不再覆盖状态栏
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//需要设置这个 flag 才能调用 setStatusBarColor 来设置状态栏颜色
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//设置状态栏颜色
window.setStatusBarColor(statusColor);
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
int color = COLOR_DEFAULT;
ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
if (statusColor != INVALID_VAL)
{
color = statusColor;
}
View statusBarView = activity.findViewById(R.id.status_bar);
if (statusBarView == null){
statusBarView = new View(activity);
statusBarView.setId(R.id.status_bar);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
getStatusBarHeight(activity));
contentView.addView(statusBarView, lp);
}
statusBarView.setBackgroundColor(color);
}
}
public static void compat(Activity activity)
{
compat(activity, INVALID_VAL);
}
public static int getStatusBarHeight(Context context)
{
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0)
{
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}

@ -0,0 +1,30 @@
package xyz.fycz.myreader.util.media;
import android.content.Context;
import android.os.Bundle;
import androidx.loader.content.CursorLoader;
/**
* Created by newbiechen on 2018/1/14.
*/
public class LoaderCreator {
public static final int ALL_BOOK_FILE = 1;
public static CursorLoader create(Context context, int id, Bundle bundle) {
LocalFileLoader loader = null;
switch (id){
case ALL_BOOK_FILE:
loader = new LocalFileLoader(context);
break;
default:
loader = null;
break;
}
if (loader != null) {
return loader;
}
throw new IllegalArgumentException("The id of Loader is invalid!");
}
}

@ -0,0 +1,137 @@
package xyz.fycz.myreader.util.media;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.loader.content.CursorLoader;
import java.io.File;
import java.sql.Blob;
import java.util.ArrayList;
import java.util.List;
/**
* Created by newbiechen on 2018/1/14.
*/
public class LocalFileLoader extends CursorLoader {
private static final String TAG = "LocalFileLoader";
private static final Uri FILE_URI = Uri.parse("content://media/external/file");
private static final String SELECTION = MediaStore.Files.FileColumns.DATA + " like ?";
private static final String SEARCH_TYPE = "%.txt";
private static final String SORT_ORDER = MediaStore.Files.FileColumns.DISPLAY_NAME + " DESC";
private static final String[] FILE_PROJECTION = {
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.DISPLAY_NAME
};
public LocalFileLoader(Context context) {
super(context);
initLoader();
}
/**
* Cursor 设置默认参数
*/
private void initLoader() {
setUri(FILE_URI);
setProjection(FILE_PROJECTION);
setSelection(SELECTION);
setSelectionArgs(new String[]{SEARCH_TYPE});
setSortOrder(SORT_ORDER);
}
public void parseData(Cursor cursor, final MediaStoreHelper.MediaResultCallback resultCallback) {
List<File> files = new ArrayList<>();
// 判断是否存在数据
if (cursor == null) {
// TODO:当媒体库没有数据的时候,需要做相应的处理
// 暂时直接返回空数据
resultCallback.onResultCallback(files);
return;
}
// 重复使用Loader时,需要重置cursor的position;
cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
String path;
path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA));
// 路径无效
if (TextUtils.isEmpty(path)) {
continue;
} else {
File file = new File(path);
if (file.isDirectory() || !file.exists()){
continue;
}
else {
files.add(file);
}
}
}
if (resultCallback != null) {
resultCallback.onResultCallback(files);
}
}
/**
* 从Cursor中读取对应columnName的值
*
* @param cursor
* @param columnName
* @param defaultValue
* @return 当columnName无效时返回默认值
*/
protected Object getValueFromCursor(@NonNull Cursor cursor, String columnName, Object defaultValue) {
try {
int index = cursor.getColumnIndexOrThrow(columnName);
int type = cursor.getType(index);
switch (type) {
case Cursor.FIELD_TYPE_STRING:
// TO SOLVE:某些手机的数据库将数值类型存为String类型
String value = cursor.getString(index);
try {
if (defaultValue instanceof String) {
return value;
} else if (defaultValue instanceof Long) {
return Long.valueOf(value);
} else if (defaultValue instanceof Integer) {
return Integer.valueOf(value);
} else if (defaultValue instanceof Double) {
return Double.valueOf(value);
} else if (defaultValue instanceof Float) {
return Float.valueOf(value);
}
} catch (NumberFormatException e) {
return defaultValue;
}
case Cursor.FIELD_TYPE_INTEGER:
if (defaultValue instanceof Long) {
return cursor.getLong(index);
} else if (defaultValue instanceof Integer) {
return cursor.getInt(index);
}
case Cursor.FIELD_TYPE_FLOAT:
if (defaultValue instanceof Float) {
return cursor.getFloat(index);
} else if (defaultValue instanceof Double) {
return cursor.getDouble(index);
}
case Cursor.FIELD_TYPE_BLOB:
if (defaultValue instanceof Blob) {
return cursor.getBlob(index);
}
case Cursor.FIELD_TYPE_NULL:
default:
return defaultValue;
}
} catch (IllegalArgumentException e) {
return defaultValue;
}
}
}

@ -0,0 +1,68 @@
package xyz.fycz.myreader.util.media;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import androidx.fragment.app.FragmentActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.List;
/**
* Created by newbiechen on 2018/1/14.
* 获取媒体库的数据
*/
public class MediaStoreHelper {
/**
* 获取媒体库中所有的书籍文件
* <p>
* 暂时只支持 TXT
*
* @param activity
* @param resultCallback
*/
public static void getAllBookFile(FragmentActivity activity, MediaResultCallback resultCallback) {
// 将文件的获取处理交给 LoaderManager。
activity.getSupportLoaderManager()
.initLoader(LoaderCreator.ALL_BOOK_FILE, null, new MediaLoaderCallbacks(activity, resultCallback));
}
public interface MediaResultCallback {
void onResultCallback(List<File> files);
}
/**
* Loader 回调处理
*/
static class MediaLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
protected WeakReference<Context> mContext;
protected MediaResultCallback mResultCallback;
public MediaLoaderCallbacks(Context context, MediaResultCallback resultCallback) {
mContext = new WeakReference<>(context);
mResultCallback = resultCallback;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return LoaderCreator.create(mContext.get(), id, args);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
LocalFileLoader localFileLoader = (LocalFileLoader) loader;
localFileLoader.parseData(data, mResultCallback);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
}
}

@ -0,0 +1,93 @@
package xyz.fycz.myreader.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by newbiechen on 2017/10/8.
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final String TAG = "DividerItemDecoration";
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDrawable;
public DividerItemDecoration(Context context) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDrawable = a.getDrawable(0);
a.recycle();
}
@Override
public void onDraw(Canvas c, RecyclerView parent) {
if (getLayoutManagerType(parent) == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
private int getLayoutManagerType(RecyclerView rv){
RecyclerView.LayoutManager manager = rv.getLayoutManager();
if (!(manager instanceof LinearLayoutManager)){
throw new IllegalArgumentException("only supply linearLayoutManager");
}
return ((LinearLayoutManager) manager).getOrientation();
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDrawable.getIntrinsicHeight();
mDrawable.setBounds(left, top, right, bottom);
mDrawable.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDrawable.getIntrinsicHeight();
mDrawable.setBounds(left, top, right, bottom);
mDrawable.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (getLayoutManagerType(parent) == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDrawable.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDrawable.getIntrinsicWidth(), 0);
}
}
}

@ -0,0 +1,267 @@
package xyz.fycz.myreader.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import xyz.fycz.myreader.R;
/**
* Created by newbiechen on 17-4-22.
* 功能:
* 1. 加载动画
* 2. 加载错误点击重新加载
*/
public class RefreshLayout extends FrameLayout {
private static final String TAG = "RefreshLayout";
protected static final int STATUS_LOADING = 0;
protected static final int STATUS_FINISH = 1;
protected static final int STATUS_ERROR = 2;
protected static final int STATUS_EMPTY = 3;
private Context mContext;
private int mEmptyViewId;
private int mErrorViewId;
private int mLoadingViewId;
private View mEmptyView;
private View mErrorView;
private View mLoadingView;
private View mContentView;
private OnReloadingListener mListener;
private int mStatus = 0;
public RefreshLayout(Context context) {
this(context,null);
}
public RefreshLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public RefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
initAttrs(attrs);
initView();
}
private void initAttrs(AttributeSet attrs){
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.RefreshLayout);
mEmptyViewId = typedArray.getResourceId(R.styleable.RefreshLayout_layout_refresh_empty,R.layout.view_empty);
mErrorViewId = typedArray.getResourceId(R.styleable.RefreshLayout_layout_refresh_error,R.layout.view_net_error);
mLoadingViewId = typedArray.getResourceId(R.styleable.RefreshLayout_layout_refresh_loading,R.layout.view_loading);
typedArray.recycle();
}
private void initView(){
//添加在empty、error、loading 情况下的布局
mEmptyView = inflateView(mEmptyViewId);
mErrorView = inflateView(mErrorViewId);
mLoadingView = inflateView(mLoadingViewId);
addView(mEmptyView);
addView(mErrorView);
addView(mLoadingView);
//设置监听器
mErrorView.setOnClickListener(
(view) -> {
if (mListener != null){
toggleStatus(STATUS_LOADING);
mListener.onReload();
}
}
);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
toggleStatus(STATUS_LOADING);
}
@Override
public void onViewAdded(View child) {
super.onViewAdded(child);
if (getChildCount() == 4){
mContentView = child;
}
}
//除了自带的数据,保证子类只能够添加一个子View
@Override
public void addView(View child) {
if (getChildCount() > 4) {
throw new IllegalStateException("RefreshLayout can host only one direct child");
}
super.addView(child);
}
@Override
public void addView(View child, int index) {
if (getChildCount() > 4) {
throw new IllegalStateException("RefreshLayout can host only one direct child");
}
super.addView(child, index);
}
@Override
public void addView(View child, ViewGroup.LayoutParams params) {
if (getChildCount() > 4) {
throw new IllegalStateException("RefreshLayout can host only one direct child");
}
super.addView(child, params);
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (getChildCount() > 4) {
throw new IllegalStateException("RefreshLayout can host only one direct child");
}
super.addView(child, index, params);
}
public void showLoading(){
if (mStatus != STATUS_LOADING){
toggleStatus(STATUS_LOADING);
}
}
public void showFinish(){
if (mStatus == STATUS_LOADING){
toggleStatus(STATUS_FINISH);
}
}
public void showError(){
if (mStatus != STATUS_ERROR){
toggleStatus(STATUS_ERROR);
}
}
public void showEmpty(){
if (mStatus != STATUS_EMPTY){
toggleStatus(STATUS_EMPTY);
}
}
//视图根据状态切换
private void toggleStatus(int status){
switch (status){
case STATUS_LOADING:
mLoadingView.setVisibility(VISIBLE);
mEmptyView.setVisibility(GONE);
mErrorView.setVisibility(GONE);
if (mContentView != null){
mContentView.setVisibility(GONE);
}
break;
case STATUS_FINISH:
if (mContentView != null){
mContentView.setVisibility(VISIBLE);
}
mLoadingView.setVisibility(GONE);
mEmptyView.setVisibility(GONE);
mErrorView.setVisibility(GONE);
break;
case STATUS_ERROR:
mErrorView.setVisibility(VISIBLE);
mLoadingView.setVisibility(GONE);
mEmptyView.setVisibility(GONE);
if (mContentView != null){
mContentView.setVisibility(GONE);
}
break;
case STATUS_EMPTY:
mEmptyView.setVisibility(VISIBLE);
mErrorView.setVisibility(GONE);
mLoadingView.setVisibility(GONE);
if (mContentView != null){
mContentView.setVisibility(GONE);
}
break;
}
mStatus = status;
}
protected int getStatus(){
return mStatus;
}
public void setOnReloadingListener(OnReloadingListener listener){
mListener = listener;
}
private View inflateView(int id){
return LayoutInflater.from(mContext)
.inflate(id,this,false);
}
//数据存储
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superParcel = super.onSaveInstanceState();
SavedState savedState = new SavedState(superParcel);
savedState.status = mStatus;
return savedState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
//刷新状态
toggleStatus(savedState.status);
}
static class SavedState extends BaseSavedState {
int status;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
status = in.readInt();
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(status);
}
public static final Creator<SavedState> CREATOR
= new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
//添加错误重新加载的监听
public interface OnReloadingListener{
void onReload();
}
}

@ -346,9 +346,9 @@ public class PageView extends View {
}
// 获取具体的加载器
if ("本地书籍".equals(collBook.getType())){
mPageLoader = new LocalPageLoader(this, collBook, new ChapterService(), setting);
mPageLoader = new LocalPageLoader(this, collBook, ChapterService.getInstance(), setting);
}else {
mPageLoader = new NetPageLoader(this, collBook, new ChapterService(), mReadCrawler, setting);
mPageLoader = new NetPageLoader(this, collBook, ChapterService.getInstance(), mReadCrawler, setting);
}
// 判断是否 PageView 已经初始化完成
if (mViewWidth != 0 || mViewHeight != 0) {

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray" android:state_enabled="false" />
<item android:color="@color/white" android:state_pressed="true" />
<item android:color="@color/colorAccent"/>
</selector>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray" android:state_enabled="false" />
<item android:color="@color/white" android:state_pressed="true" />
<item android:color="@color/black"/>
</selector>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray" android:state_enabled="false" />
<item android:color="@color/black" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1007 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="680.31"
android:viewportWidth="680.31">
<path
android:fillColor="#2C2C2C"
android:pathData="M339.865,79.038c-147.014,0-266.518,119.198-266.518,266.519 c0,147.011,119.198,266.519,266.518,266.519c147.013,0,266.52-119.201,266.52-266.519 C606.078,198.542,486.878,79.038,339.865,79.038L339.865,79.038z M339.865,587.929c-133.871,0-242.373-108.505-242.373-242.373 c0-133.872,108.502-242.066,242.373-242.066c133.868,0,242.372,108.5,242.372,242.373 C582.237,479.731,473.432,587.929,339.865,587.929L339.865,587.929z M339.865,587.929" />
<path
android:fillColor="#2C2C2C"
android:pathData="M305.329,491.346c20.782,0,28.118,33.619,28.118,33.619h12.836c0,0,7.337-33.619,28.12-33.619h133.869 c7.95,0,14.673-6.725,14.673-14.671V242.25c0-33.316-33.316-33.316-33.316-33.316H361.261c-10.697,0-21.396,13.449-21.396,20.479 c0-7.03-10.391-20.479-21.088-20.479H190.103c0,0-33.317,0-33.317,33.316v234.426c0,8.253,6.418,14.671,14.67,14.671H305.329z M348.117,236.75c0-6.115,4.89-11.31,11.005-11.31h139.065c3.973,0,7.336,3.361,7.336,7.64l0.607,235.647 c0,4.279-3.36,7.644-7.333,7.644H372.873c-14.06,0-24.756,15.589-24.756,15.589V236.75z M173.902,233.08 c0-4.279,3.364-7.64,7.336-7.64h139.066c6.112,0,11.003,4.891,11.003,11.31v254.903c0,0-11.003-15.589-24.759-15.589H180.627 c-3.974,0-7.337-3.36-7.337-7.639L173.902,233.08z M173.902,233.08" />
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#707070"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z" />
</vector>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="3dp"/>
<solid android:color="@color/light_blue"/>
</shape>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/shape_blue" android:state_pressed="true"/>
<item android:drawable="@drawable/shape_unclick" android:state_enabled="false"/>
<item android:drawable="@drawable/shape_corner_blue"/>
</selector>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/shape_blue" android:state_pressed="true"/>
<item android:drawable="@drawable/shape_unclick" android:state_enabled="false"/>
<item android:drawable="@drawable/shape_corner_black" />
</selector>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_cb_file_selected" android:state_pressed="true"/>
<item android:drawable="@drawable/ic_cb_file_selected" android:state_checked="true"/>
<item android:drawable="@drawable/ic_cb_file_normal"/>
</selector>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@color/widget_button_selected"/>
<item android:state_selected="true"
android:drawable="@color/widget_button_selected"/>
<item
android:drawable="@color/widget_button_normal"/>
</selector>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/pwd_visiable" android:state_checked="true"/>
<item android:drawable="@mipmap/pwd_gone" android:state_checked="false"/>
<item android:drawable="@mipmap/pwd_gone" />
</selector>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="3dp"/>
<solid android:color="@color/colorAccent" />
</shape>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:color="@color/black" android:width="1dp"/>
<corners android:radius="3dp"/>
</shape>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:color="@color/colorAccent" android:width="1dp"/>
<corners android:radius="3dp"/>
</shape>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:color="@color/gray" android:width="1dp"/>
<corners android:radius="3dp" />
</shape>

@ -31,7 +31,7 @@
android:layout_width="80dp"
android:layout_height="120dp"
android:scaleType="fitXY"
app:srcCompat="@mipmap/no_image"/>
app:srcCompat="@mipmap/default_cover"/>
<RelativeLayout
android:layout_width="match_parent"

@ -19,6 +19,8 @@
android:layout_height="30dp"
android:paddingTop="4dp"
android:layout_gravity="center_horizontal"
android:layout_alignParentStart="true"
android:layout_centerInParent="true"
android:layout_margin="5dip"
app:srcCompat="@mipmap/larrow_black"/>

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--指示器-->
<include layout="@layout/scroll_title_indicator"/>
<androidx.viewpager.widget.ViewPager
android:id="@+id/tab_vp"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</androidx.viewpager.widget.ViewPager>
<!--文件点击按钮-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="52dp"
android:paddingRight="10dp"
android:gravity="center_vertical">
<CheckBox
android:id="@+id/file_system_cb_selected_all"
android:layout_width="120dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:text="全选"
android:textSize="15dp"
android:textColor="@color/selector_cb_file"
android:theme="@style/MyCheckBox" />
<Button
android:id="@+id/file_system_btn_add_book"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:minWidth="110dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@drawable/selector_btn_add"
android:textColor="@color/selector_btn_file_add"
android:text="加入书架" />
<Button
android:id="@+id/file_system_btn_delete"
android:layout_width="70dp"
android:layout_height="35dp"
android:layout_centerVertical="true"
android:layout_marginRight="15dp"
android:background="@drawable/selector_btn_add"
android:textColor="@color/selector_btn_file_add"
android:layout_toLeftOf="@id/file_system_btn_add_book"
android:text="删除"/>
</RelativeLayout>
</LinearLayout>

@ -119,11 +119,10 @@
android:textSize="20dp" />
<!--添加输入框-->
<EditText
<!--<EditText
android:id="@+id/et_user"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:background="@drawable/editview_shape"
android:drawableStart="@drawable/iconfont_user"
android:drawablePadding="10dp"
@ -131,23 +130,56 @@
android:inputType="text"
android:maxLines="1"
android:paddingStart="10dp"
android:textSize="18dp" />
android:textSize="18dp" />-->
<EditText
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_user"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="14">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_account_tip"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>
<!--<EditText
android:id="@+id/et_password"
style="@style/EditTextStyle"
android:background="@drawable/editview_shape"
android:drawableStart="@drawable/iconfont_password"
android:hint="@string/et_password_tip"
android:inputType="textPassword"
android:textSize="18dp" />
android:textSize="18dp" />-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="16"
app:passwordToggleEnabled="true"
app:passwordToggleTintMode="src_in">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_password_tip"
android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
<!--<EditText
android:id="@+id/et_captcha"
style="@style/EditTextStyle"
android:layout_width="match_parent"
@ -155,7 +187,23 @@
android:background="@drawable/editview_shape"
android:hint="@string/et_rp_captcha"
android:inputType="text"
android:textSize="18dp" />
android:textSize="18dp" />-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_captcha"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="4">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_rp_captcha"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/iv_captcha"
@ -163,7 +211,6 @@
android:layout_height="wrap_content"
android:layout_weight="3"
android:layout_gravity="center_vertical"
android:layout_marginTop="10dp"
android:paddingStart="7dp" />
</LinearLayout>

@ -70,49 +70,82 @@
android:textColor="@color/sys_red"
android:visibility="gone" />
<!--添加输入框-->
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:background="@drawable/editview_shape"
android:drawablePadding="10dp"
android:hint="@string/et_account_tip"
android:inputType="text"
android:maxLines="1"
android:paddingStart="10dp"
android:textSize="18dp" />
<EditText
android:id="@+id/et_password"
style="@style/EditTextStyle"
android:background="@drawable/editview_shape"
android:hint="@string/et_password_tip"
android:inputType="textPassword"
android:textSize="18dp" />
<EditText
android:id="@+id/et_rp_password"
style="@style/EditTextStyle"
android:background="@drawable/editview_shape"
android:hint="@string/et_rp_password_tip"
android:inputType="textPassword"
android:textSize="18dp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="14">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_account_tip"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="16"
app:passwordToggleEnabled="true"
app:passwordToggleTintMode="src_in">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_password_tip"
android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_rp_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="16"
app:passwordToggleEnabled="true"
app:passwordToggleTintMode="src_in">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_password_tip"
android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/et_captcha"
style="@style/EditTextStyle"
android:background="@drawable/editview_shape"
android:layout_width="match_parent"
android:layout_weight="1"
android:hint="@string/et_rp_captcha"
android:inputType="text"
android:textSize="18dp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_captcha"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="4">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_rp_captcha"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/iv_captcha"
@ -120,7 +153,6 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="3"
android:layout_marginTop="10dp"
android:paddingStart="7dp" />
</LinearLayout>

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--path-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="35dp"
android:orientation="horizontal">
<TextView
style="@style/SingleLine"
android:id="@+id/file_category_tv_path"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:padding="10dp"
android:textSize="12dp"
android:textColor="@color/text_common_h2"
tools:text="存储卡:/"/>
<View
android:layout_width="2dp"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:background="@color/divider_narrow"/>
<TextView
android:id="@+id/file_category_tv_back_last"
android:layout_width="80dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="上一级"
android:textSize="12dp"
android:drawableLeft="@drawable/ic_back_last"
android:textColor="@color/text_common_h2"/>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/file_category_rv_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<xyz.fycz.myreader.widget.RefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/local_book_rv_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
</xyz.fycz.myreader.widget.RefreshLayout>

@ -16,7 +16,7 @@
android:layout_width="64dp"
android:layout_height="88dp"
android:scaleType="fitXY"
app:srcCompat="@mipmap/no_image"/>
app:srcCompat="@mipmap/default_cover"/>
<LinearLayout android:id="@+id/ll_book_read"
android:layout_width="match_parent"

@ -18,7 +18,7 @@
android:layout_width="80dp"
android:layout_height="110dp"
android:scaleType="fitXY"
app:srcCompat="@mipmap/no_image"/>
app:srcCompat="@mipmap/default_cover"/>
<TextView
android:layout_marginTop="5dp"

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:background="@drawable/selector_common_bg"
android:layout_height="60dp">
<FrameLayout
android:id="@+id/file_fl_icon"
android:layout_width="60dp"
android:layout_height="match_parent">
<!--文件夹标识或已选文件标识-->
<ImageView
android:id="@+id/file_iv_icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"
android:visibility="gone"
tools:src="@drawable/ic_folder"/>
<!--选择是否添加文件-->
<CheckBox
android:id="@+id/file_cb_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:enabled="true"
android:clickable="false"
android:focusable="true"
android:theme="@style/MyCheckBox" />
</FrameLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
style="@style/SingleLine"
android:id="@+id/file_tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="@color/title_black"
android:text="英语四级"/>
<LinearLayout
android:id="@+id/file_ll_brief"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_alignParentBottom="true">
<TextView
style="@style/File_Tag"
android:id="@+id/file_tv_tag"
android:layout_toRightOf="@+id/file_fl_icon"
android:layout_alignParentBottom="true"
android:layout_marginRight="15dp"
android:text="TXT"
android:background="@drawable/ic_tag_txt"/>
<TextView
android:id="@+id/file_tv_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/title_black"
android:layout_marginRight="15dp"
tools:text="324kb"/>
<TextView
android:id="@+id/file_tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/title_black"
tools:text="2017-05-22"/>
</LinearLayout>
<TextView
android:id="@+id/file_tv_sub_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
tools:text="0 项"
android:textColor="@color/title_black"
android:visibility="gone"/>
</RelativeLayout>
</LinearLayout>

@ -12,7 +12,7 @@
android:layout_width="70dp"
android:layout_height="100dp"
android:scaleType="fitXY"
app:srcCompat="@mipmap/no_image" />
app:srcCompat="@mipmap/default_cover" />
<RelativeLayout
android:layout_width="match_parent"

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/white"
android:theme="@style/Theme.ToolBar.Menu"
app:layout_scrollFlags="scroll|enterAlways">
</androidx.appcompat.widget.Toolbar>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_tl_indicator"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@color/white"
app:tabTextColor="@color/title_black"
app:tabSelectedTextColor="@color/colorAccent"
app:tabIndicatorColor="@color/colorAccent">
</com.google.android.material.tabs.TabLayout>
</com.google.android.material.appbar.AppBarLayout>
</merge>

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2016 JustWayward Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rl_empty_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="@drawable/ic_no_data" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:text="暂无数据"
android:textColor="@color/text_common_h1" />
</LinearLayout>
</RelativeLayout>

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/loadding_pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
</RelativeLayout>

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rl_empty_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical">
<ImageView
android:id="@+id/book_shelf_iv_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="@drawable/ic_no_data" />
<TextView
android:id="@+id/tvEmptyView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:text="网络连接失败..."
android:textColor="@color/text_common_h2" />
</LinearLayout>
</RelativeLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -1,7 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RefreshLayout">
<attr name="layout_refresh_empty" format="reference"/>
<attr name="layout_refresh_error" format="reference"/>
<attr name="layout_refresh_loading" format="reference"/>
</declare-styleable>
<declare-styleable name="Animation_emerge">
<attr name="windowEnterAnimation" format="integer" />

@ -141,4 +141,23 @@
<color name="highlight">#d3321b</color>
<color name="darker_gray">#aaaaaaaa</color>
<!--通用一级标题的颜色-->
<color name="text_common_h1">#212121</color>
<!--通用二级标题的颜色-->
<color name="text_common_h2">#727272</color>
<!--通用三级标题的颜色-->
<color name="text_common_h3">#B2B2B2</color>
<!--通用分割线细-->
<color name="divider_narrow">#E3E3E3</color>
<!--button-->
<color name="widget_button_normal">@color/white</color>
<color name="widget_button_selected">#E3E3E3</color>
<color name="divider_common">#727272</color>
<color name="translucent_text">#96ffffff</color>
</resources>

@ -3,4 +3,5 @@
<item name="first" type="id" />
<item type="id" name="status_bar"/>
</resources>

@ -86,4 +86,11 @@
<string name="this_github_url">https://github.com/fengyuecanzhu/FYReader</string>
<string name="update">检查更新</string>
<!--file system-->
<string name="file_sub_count">%1$d 项</string>
<string name="file_path">存储卡:%1$s</string>
<string name="file_add_shelf">加入书架</string>
<string name="file_add_shelves">加入书架(%1$d)</string>
<string name="file_add_succeed">成功添加%1$d本书</string>
</resources>

@ -42,4 +42,56 @@
<item name="colorControlNormal">@color/gray</item>
<item name="colorControlActivated">@color/colorAccent</item>
</style>
<style name="SingleLine">
<item name="android:singleLine">true</item>
<item name="android:maxLines">1</item>
<item name="android:ellipsize">end</item>
</style>
<style name="File_Tag">
<item name="android:layout_width">38dp</item>
<item name="android:layout_height">18dp</item>
<item name="android:textSize">12sp</item>
<item name="android:textColor">@color/white</item>
<item name="android:textStyle">bold</item>
<item name="android:gravity">center</item>
</style>
<!--Toolbar-->
<style name="Theme.ToolBar.Menu" parent="ThemeOverlay.AppCompat.ActionBar">
<!-- 设置-弹窗的背景-颜色-->
<item name="android:colorBackground">@color/white</item>
<!-- 字体的颜色-->
<item name="android:textColor">@color/black</item>
<!-- 字体大小-->
<item name="android:textSize">16sp</item>
<item name="android:gravity">center_vertical</item>
<!-- 用于替换菜单的三个小白点,换成自已的图片-->
<!-- <item name="actionOverflowButtonStyle">@style/ActionButton.Overflow.Menu</item>-->
<!--用于控制menu弹出的位置,位于toolbar的下面,而不是上面-->
<item name="actionOverflowMenuStyle">@style/OverflowMenuStyle</item>
<!-- 用于修改item默认的分割线-->
<item name="android:dropDownListViewStyle">@style/dropDownStyle</item>
<item name="dropDownListViewStyle">@style/dropDownStyle</item>
</style>
<style name="ActionButton.Overflow.Menu" parent="android:style/Widget.Holo.Light.ActionButton.Overflow">
<item name="android:src">@drawable/ic_menu_overflow</item>
</style>
<style name="OverflowMenuStyle" parent="Widget.AppCompat.Light.PopupMenu.Overflow">
<item name="overlapAnchor">false</item>
</style>
<!--用于修改popmenu的分割线-->
<style name="dropDownStyle" parent="android:style/Widget.Holo.ListView.DropDown">
<item name="android:listSelector">@drawable/selector_common_bg</item>
<item name="android:divider">@color/divider_common</item>
<item name="android:dividerHeight">0.5dp</item>
</style>
</resources>

@ -1,2 +1,2 @@
#Fri Aug 07 09:42:27 CST 2020
VERSION_CODE=140
#Wed Aug 12 21:27:50 CST 2020
VERSION_CODE=141

Loading…
Cancel
Save