From 5f73042209534a592bbdc0ba49069913b9f44917 Mon Sep 17 00:00:00 2001 From: fengyuecanzhu <1021300691@qq.com> Date: Fri, 14 Aug 2020 17:25:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 23 +- app/src/main/assets/updatelog.fy | 6 + .../myreader/backup/BackupAndRestore.java | 2 +- .../xyz/fycz/myreader/base/BaseActivity.java | 4 - .../xyz/fycz/myreader/base/BaseActivity2.java | 127 ++++++++ .../xyz/fycz/myreader/base/BaseFragment.java | 104 +++++++ .../fycz/myreader/base/BaseListAdapter.java | 154 ++++++++++ .../fycz/myreader/base/BaseTabActivity.java | 86 ++++++ .../fycz/myreader/base/BaseViewHolder.java | 19 ++ .../xyz/fycz/myreader/base/IViewHolder.java | 16 ++ .../fycz/myreader/base/ViewHolderImpl.java | 43 +++ .../xyz/fycz/myreader/common/APPCONST.java | 4 + .../controller/SyncChaptersController.java | 2 +- .../fycz/myreader/creator/DialogCreator.java | 13 +- .../greendao/service/BookMarkService.java | 13 +- .../greendao/service/BookService.java | 36 ++- .../greendao/service/ChapterService.java | 11 + .../service/SearchHistoryService.java | 12 +- .../ui/bookinfo/BookInfoPresenter.java | 2 +- .../myreader/ui/filesys/BaseFileFragment.java | 87 ++++++ .../ui/filesys/FileCategoryFragment.java | 176 ++++++++++++ .../fycz/myreader/ui/filesys/FileHolder.java | 103 +++++++ .../ui/filesys/FileSystemActivity.java | 266 +++++++++++++++++ .../ui/filesys/FileSystemAdapter.java | 148 ++++++++++ .../ui/filesys/LocalBookFragment.java | 89 ++++++ .../fycz/myreader/ui/font/FontsPresenter.java | 18 +- .../fycz/myreader/ui/home/MainActivity.java | 23 ++ .../fycz/myreader/ui/home/MainPresenter.java | 3 + .../ui/home/bookcase/BookcaseAdapter.java | 4 +- .../bookcase/BookcaseDetailedAdapter.java | 7 +- .../ui/home/bookcase/BookcasePresenter.java | 45 ++- .../fycz/myreader/ui/read/ReadPresenter.java | 6 +- .../catalog/CatalogActivityPresenter.java | 7 +- .../ui/read/catalog/CatalogPresenter.java | 2 +- .../ui/search/SearchBookPrensenter.java | 2 +- .../fycz/myreader/ui/user/LoginActivity.java | 19 +- .../fycz/myreader/ui/user/LoginPresenter.java | 84 +++--- .../myreader/ui/user/RegisterActivity.java | 17 +- .../myreader/ui/user/RegisterPresenter.java | 4 + .../xyz/fycz/myreader/util/FileStack.java | 49 ++++ .../xyz/fycz/myreader/util/ImageUtil.java | 272 ++++++++++++++++++ .../fycz/myreader/util/StatusBarCompat.java | 71 +++++ .../myreader/util/media/LoaderCreator.java | 30 ++ .../myreader/util/media/LocalFileLoader.java | 137 +++++++++ .../myreader/util/media/MediaStoreHelper.java | 68 +++++ .../widget/DividerItemDecoration.java | 93 ++++++ .../fycz/myreader/widget/RefreshLayout.java | 267 +++++++++++++++++ .../fycz/myreader/widget/page/PageView.java | 4 +- .../main/res/color/selector_btn_file_add.xml | 6 + .../res/color/selector_btn_file_delete.xml | 6 + app/src/main/res/color/selector_cb_file.xml | 5 + .../main/res/drawable-xhdpi/ic_back_last.png | Bin 0 -> 616 bytes .../res/drawable-xhdpi/ic_cb_file_normal.png | Bin 0 -> 686 bytes .../drawable-xhdpi/ic_cb_file_selected.png | Bin 0 -> 696 bytes .../res/drawable-xhdpi/ic_file_loaded.png | Bin 0 -> 1007 bytes .../main/res/drawable-xhdpi/ic_no_data.png | Bin 0 -> 5615 bytes .../res/drawable-xxhdpi/ic_menu_overflow.png | Bin 0 -> 205 bytes app/src/main/res/drawable/ic_book_has.xml | 14 + app/src/main/res/drawable/ic_folder.xml | 10 + app/src/main/res/drawable/ic_tag_txt.xml | 5 + .../main/res/drawable/selector_btn_add.xml | 6 + .../main/res/drawable/selector_btn_delete.xml | 6 + .../main/res/drawable/selector_cb_file.xml | 6 + .../main/res/drawable/selector_common_bg.xml | 10 + app/src/main/res/drawable/selector_pwd.xml | 6 + app/src/main/res/drawable/shape_blue.xml | 6 + .../main/res/drawable/shape_corner_black.xml | 6 + .../main/res/drawable/shape_corner_blue.xml | 6 + app/src/main/res/drawable/shape_unclick.xml | 6 + .../main/res/layout/activity_book_info.xml | 2 +- app/src/main/res/layout/activity_catalog.xml | 2 + .../main/res/layout/activity_file_system.xml | 60 ++++ app/src/main/res/layout/activity_login.xml | 63 +++- app/src/main/res/layout/activity_register.xml | 108 ++++--- .../res/layout/fragment_file_category.xml | 48 ++++ .../main/res/layout/fragment_local_book.xml | 12 + .../layout/gridview_book_detailed_item.xml | 2 +- .../main/res/layout/gridview_book_item.xml | 2 +- app/src/main/res/layout/item_file.xml | 91 ++++++ .../layout/listview_book_store_book_item.xml | 2 +- .../res/layout/scroll_title_indicator.xml | 28 ++ app/src/main/res/layout/view_empty.xml | 44 +++ app/src/main/res/layout/view_loading.xml | 11 + app/src/main/res/layout/view_net_error.xml | 31 ++ .../main/res/mipmap-xhdpi/default_cover.jpg | Bin 0 -> 64772 bytes app/src/main/res/mipmap-xhdpi/pwd_gone.png | Bin 0 -> 920 bytes .../main/res/mipmap-xhdpi/pwd_visiable.png | Bin 0 -> 1428 bytes app/src/main/res/values/attrs.xml | 6 +- app/src/main/res/values/colors.xml | 19 ++ app/src/main/res/values/ids.xml | 1 + app/src/main/res/values/strings.xml | 7 + app/src/main/res/values/styles.xml | 52 ++++ app/version_code.properties | 4 +- 93 files changed, 3295 insertions(+), 182 deletions(-) create mode 100644 app/src/main/java/xyz/fycz/myreader/base/BaseActivity2.java create mode 100644 app/src/main/java/xyz/fycz/myreader/base/BaseFragment.java create mode 100644 app/src/main/java/xyz/fycz/myreader/base/BaseListAdapter.java create mode 100644 app/src/main/java/xyz/fycz/myreader/base/BaseTabActivity.java create mode 100644 app/src/main/java/xyz/fycz/myreader/base/BaseViewHolder.java create mode 100644 app/src/main/java/xyz/fycz/myreader/base/IViewHolder.java create mode 100644 app/src/main/java/xyz/fycz/myreader/base/ViewHolderImpl.java create mode 100644 app/src/main/java/xyz/fycz/myreader/ui/filesys/BaseFileFragment.java create mode 100644 app/src/main/java/xyz/fycz/myreader/ui/filesys/FileCategoryFragment.java create mode 100644 app/src/main/java/xyz/fycz/myreader/ui/filesys/FileHolder.java create mode 100644 app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemActivity.java create mode 100644 app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemAdapter.java create mode 100644 app/src/main/java/xyz/fycz/myreader/ui/filesys/LocalBookFragment.java create mode 100644 app/src/main/java/xyz/fycz/myreader/util/FileStack.java create mode 100644 app/src/main/java/xyz/fycz/myreader/util/ImageUtil.java create mode 100644 app/src/main/java/xyz/fycz/myreader/util/StatusBarCompat.java create mode 100644 app/src/main/java/xyz/fycz/myreader/util/media/LoaderCreator.java create mode 100644 app/src/main/java/xyz/fycz/myreader/util/media/LocalFileLoader.java create mode 100644 app/src/main/java/xyz/fycz/myreader/util/media/MediaStoreHelper.java create mode 100644 app/src/main/java/xyz/fycz/myreader/widget/DividerItemDecoration.java create mode 100644 app/src/main/java/xyz/fycz/myreader/widget/RefreshLayout.java create mode 100644 app/src/main/res/color/selector_btn_file_add.xml create mode 100644 app/src/main/res/color/selector_btn_file_delete.xml create mode 100644 app/src/main/res/color/selector_cb_file.xml create mode 100644 app/src/main/res/drawable-xhdpi/ic_back_last.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_cb_file_normal.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_cb_file_selected.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_file_loaded.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_no_data.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_menu_overflow.png create mode 100644 app/src/main/res/drawable/ic_book_has.xml create mode 100644 app/src/main/res/drawable/ic_folder.xml create mode 100644 app/src/main/res/drawable/ic_tag_txt.xml create mode 100644 app/src/main/res/drawable/selector_btn_add.xml create mode 100644 app/src/main/res/drawable/selector_btn_delete.xml create mode 100644 app/src/main/res/drawable/selector_cb_file.xml create mode 100644 app/src/main/res/drawable/selector_common_bg.xml create mode 100644 app/src/main/res/drawable/selector_pwd.xml create mode 100644 app/src/main/res/drawable/shape_blue.xml create mode 100644 app/src/main/res/drawable/shape_corner_black.xml create mode 100644 app/src/main/res/drawable/shape_corner_blue.xml create mode 100644 app/src/main/res/drawable/shape_unclick.xml create mode 100644 app/src/main/res/layout/activity_file_system.xml create mode 100644 app/src/main/res/layout/fragment_file_category.xml create mode 100644 app/src/main/res/layout/fragment_local_book.xml create mode 100644 app/src/main/res/layout/item_file.xml create mode 100644 app/src/main/res/layout/scroll_title_indicator.xml create mode 100644 app/src/main/res/layout/view_empty.xml create mode 100644 app/src/main/res/layout/view_loading.xml create mode 100644 app/src/main/res/layout/view_net_error.xml create mode 100644 app/src/main/res/mipmap-xhdpi/default_cover.jpg create mode 100644 app/src/main/res/mipmap-xhdpi/pwd_gone.png create mode 100644 app/src/main/res/mipmap-xhdpi/pwd_visiable.png diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 616ccfc..c3a79fa 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,6 +27,9 @@ + + + @@ -87,15 +90,17 @@ - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/updatelog.fy b/app/src/main/assets/updatelog.fy index f1df7d4..3f3b008 100644 --- a/app/src/main/assets/updatelog.fy +++ b/app/src/main/assets/updatelog.fy @@ -1,3 +1,9 @@ +2020.08.12 +风月读书v1.20.081221 +1、新增本地书籍导入页面(不再使用系统文件管理器导入),支持一次性导入多本书籍 +2、新增智能导入(扫描)本地书籍 +3、修复书架没有书籍时退出闪退的问题 + 2020.08.07 风月读书v1.20.080709 1、修复搜索书籍滑动混乱的问题 diff --git a/app/src/main/java/xyz/fycz/myreader/backup/BackupAndRestore.java b/app/src/main/java/xyz/fycz/myreader/backup/BackupAndRestore.java index b53a1a4..a0ba02f 100644 --- a/app/src/main/java/xyz/fycz/myreader/backup/BackupAndRestore.java +++ b/app/src/main/java/xyz/fycz/myreader/backup/BackupAndRestore.java @@ -18,7 +18,7 @@ import java.util.List; public class BackupAndRestore { - BookService mBookService = new BookService(); + BookService mBookService = BookService.getInstance(); /** * 备份书架 diff --git a/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java b/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java index ee040e9..791a803 100644 --- a/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java @@ -127,8 +127,4 @@ public class BaseActivity extends AppCompatActivity { } - - - - } diff --git a/app/src/main/java/xyz/fycz/myreader/base/BaseActivity2.java b/app/src/main/java/xyz/fycz/myreader/base/BaseActivity2.java new file mode 100644 index 0000000..21e2f3e --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/base/BaseActivity2.java @@ -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 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)); + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/base/BaseFragment.java b/app/src/main/java/xyz/fycz/myreader/base/BaseFragment.java new file mode 100644 index 0000000..bceacfe --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/base/BaseFragment.java @@ -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 getViewById(int id){ + if (root == null){ + return null; + } + return (VT) root.findViewById(id); + } +} + + diff --git a/app/src/main/java/xyz/fycz/myreader/base/BaseListAdapter.java b/app/src/main/java/xyz/fycz/myreader/base/BaseListAdapter.java new file mode 100644 index 0000000..20f71a5 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/base/BaseListAdapter.java @@ -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 extends RecyclerView.Adapter{ + + private static final String TAG = "BaseListAdapter"; + /*common statement*/ + protected final List mList = new ArrayList<>(); + protected OnItemClickListener mClickListener; + protected OnItemLongClickListener mLongClickListener; + + /************************abstract area************************/ + protected abstract IViewHolder createViewHolder(int viewType); + + /*************************rewrite logic area***************************************/ + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + IViewHolder 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 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 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 value){ + mList.removeAll(value); + notifyDataSetChanged(); + } + + public T getItem(int position){ + return mList.get(position); + } + + public List getItems(){ + return Collections.unmodifiableList(mList); + } + + public int getItemSize(){ + return mList.size(); + } + + public void refreshItems(List 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); + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/base/BaseTabActivity.java b/app/src/main/java/xyz/fycz/myreader/base/BaseTabActivity.java new file mode 100644 index 0000000..2d65410 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/base/BaseTabActivity.java @@ -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 mFragmentList; + private List mTitleList; + + /**************abstract***********/ + protected abstract List createTabFragments(); + protected abstract List 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); + } + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/base/BaseViewHolder.java b/app/src/main/java/xyz/fycz/myreader/base/BaseViewHolder.java new file mode 100644 index 0000000..6f6453d --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/base/BaseViewHolder.java @@ -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 extends RecyclerView.ViewHolder{ + public IViewHolder holder; + + public BaseViewHolder(View itemView, IViewHolder holder) { + super(itemView); + this.holder = holder; + holder.initView(); + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/base/IViewHolder.java b/app/src/main/java/xyz/fycz/myreader/base/IViewHolder.java new file mode 100644 index 0000000..0e495ce --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/base/IViewHolder.java @@ -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 { + View createItemView(ViewGroup parent); + void initView(); + void onBind(T data,int pos); + void onClick(); +} diff --git a/app/src/main/java/xyz/fycz/myreader/base/ViewHolderImpl.java b/app/src/main/java/xyz/fycz/myreader/base/ViewHolderImpl.java new file mode 100644 index 0000000..b5bdabd --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/base/ViewHolderImpl.java @@ -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 implements IViewHolder { + 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 findById(int id){ + return (V) view.findViewById(id); + } + + protected Context getContext(){ + return context; + } + + protected View getItemView(){ + return view; + } + + @Override + public void onClick() { + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java b/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java index b399d81..a997a40 100644 --- a/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java +++ b/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java @@ -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"; } diff --git a/app/src/main/java/xyz/fycz/myreader/controller/SyncChaptersController.java b/app/src/main/java/xyz/fycz/myreader/controller/SyncChaptersController.java index d863cc6..fd5d851 100644 --- a/app/src/main/java/xyz/fycz/myreader/controller/SyncChaptersController.java +++ b/app/src/main/java/xyz/fycz/myreader/controller/SyncChaptersController.java @@ -19,7 +19,7 @@ public class SyncChaptersController { mLocalChapters = localChapters; mNetChapters = netChapters; mBook = book; - mChapterService = new ChapterService(); + mChapterService = ChapterService.getInstance(); } /** diff --git a/app/src/main/java/xyz/fycz/myreader/creator/DialogCreator.java b/app/src/main/java/xyz/fycz/myreader/creator/DialogCreator.java index 3eae25f..b34f597 100644 --- a/app/src/main/java/xyz/fycz/myreader/creator/DialogCreator.java +++ b/app/src/main/java/xyz/fycz/myreader/creator/DialogCreator.java @@ -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; diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/service/BookMarkService.java b/app/src/main/java/xyz/fycz/myreader/greendao/service/BookMarkService.java index 70653e3..4d6afcb 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/service/BookMarkService.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/service/BookMarkService.java @@ -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 findBookMarks(String sql, String[] selectionArgs) { ArrayList bookMarks = new ArrayList<>(); try { diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java b/app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java index a97f782..63aa097 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java @@ -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 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 diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java b/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java index 7d4d636..211035c 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java @@ -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 findChapters(String sql, String[] selectionArgs) { ArrayList chapters = new ArrayList<>(); try { diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/service/SearchHistoryService.java b/app/src/main/java/xyz/fycz/myreader/greendao/service/SearchHistoryService.java index d47708d..f35dd44 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/service/SearchHistoryService.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/service/SearchHistoryService.java @@ -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 findSearchHistorys(String sql, String[] selectionArgs) { ArrayList searchHistories = new ArrayList<>(); try { diff --git a/app/src/main/java/xyz/fycz/myreader/ui/bookinfo/BookInfoPresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/bookinfo/BookInfoPresenter.java index d788688..7eae1fe 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/bookinfo/BookInfoPresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/bookinfo/BookInfoPresenter.java @@ -69,7 +69,7 @@ public class BookInfoPresenter implements BasePresenter { public BookInfoPresenter(BookInfoActivity bookInfoActivity) { mBookInfoActivity = bookInfoActivity; - mBookService = new BookService(); + mBookService = BookService.getInstance(); } @Override diff --git a/app/src/main/java/xyz/fycz/myreader/ui/filesys/BaseFileFragment.java b/app/src/main/java/xyz/fycz/myreader/ui/filesys/BaseFileFragment.java new file mode 100644 index 0000000..56fcb0f --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ui/filesys/BaseFileFragment.java @@ -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 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 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(); + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileCategoryFragment.java b/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileCategoryFragment.java new file mode 100644 index 0000000..342379a --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileCategoryFragment.java @@ -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 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> entrys = mAdapter.getCheckMap().entrySet(); + for (Map.Entry entry:entrys){ + if (!entry.getKey().isDirectory()){ + ++count; + } + } + return count; + } + + public static class FileComparator implements Comparator { + @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; + } + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileHolder.java b/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileHolder.java new file mode 100644 index 0000000..e0d1ffc --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileHolder.java @@ -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 { + 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 mSelectedMap; + public FileHolder(HashMap 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; + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemActivity.java new file mode 100644 index 0000000..e01f262 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemActivity.java @@ -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 createTabFragments() { + mLocalFragment = new LocalBookFragment(); + mCategoryFragment = new FileCategoryFragment(); + return Arrays.asList(mLocalFragment,mCategoryFragment); + } + + @Override + protected List 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 files = mCurFragment.getCheckedFiles(); + //转换成Book,并存储 + List 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 convertBook(List files){ + List 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); + } + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemAdapter.java b/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemAdapter.java new file mode 100644 index 0000000..a20b6ea --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemAdapter.java @@ -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 { + //记录item是否被选中的Map + private HashMap mCheckMap = new HashMap<>(); + private int mCheckedCount = 0; + private BookService mBookService = new BookService(); + + @Override + protected IViewHolder createViewHolder(int viewType) { + return new FileHolder(mCheckMap); + } + + @Override + public void refreshItems(List 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 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 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> entrys = mCheckMap.entrySet(); + mCheckedCount = 0; + for (Map.Entry 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 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 getCheckedFiles(){ + List files = new ArrayList<>(); + Set> entrys = mCheckMap.entrySet(); + for (Map.Entry entry:entrys){ + if (entry.getValue()){ + files.add(entry.getKey()); + } + } + return files; + } + + public int getCheckedCount(){ + return mCheckedCount; + } + + public HashMap getCheckMap(){ + return mCheckMap; + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/ui/filesys/LocalBookFragment.java b/app/src/main/java/xyz/fycz/myreader/ui/filesys/LocalBookFragment.java new file mode 100644 index 0000000..3648e59 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ui/filesys/LocalBookFragment.java @@ -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(); + } + } + }); + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/ui/font/FontsPresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/font/FontsPresenter.java index ba39b12..e6e62d2 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/font/FontsPresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/font/FontsPresenter.java @@ -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); } } } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/home/MainActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/home/MainActivity.java index 7ece21d..1887cac 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/home/MainActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/home/MainActivity.java @@ -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(); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/home/MainPresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/home/MainPresenter.java index 6625f1e..cdd12e0 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/home/MainPresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/home/MainPresenter.java @@ -131,6 +131,9 @@ public class MainPresenter implements BasePresenter { * @return */ public boolean ismEditState(){ + if (mBookcaseFragment.getmBookcasePresenter() == null) { + return false; + } return mBookcaseFragment.getmBookcasePresenter().ismEditState(); } } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseAdapter.java b/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseAdapter.java index e30b487..a404923 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseAdapter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseAdapter.java @@ -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; } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseDetailedAdapter.java b/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseDetailedAdapter.java index 58191e3..73a5c0d 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseDetailedAdapter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseDetailedAdapter.java @@ -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("最近更新 ", "")); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcasePresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcasePresenter.java index c383927..b2253d7 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcasePresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcasePresenter.java @@ -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 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"); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/read/ReadPresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/read/ReadPresenter.java index 92fd2fa..5706418 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/read/ReadPresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/read/ReadPresenter.java @@ -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(); } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/read/catalog/CatalogActivityPresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/read/catalog/CatalogActivityPresenter.java index d38734b..e4f2522 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/read/catalog/CatalogActivityPresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/read/catalog/CatalogActivityPresenter.java @@ -26,7 +26,6 @@ public class CatalogActivityPresenter implements BasePresenter { private CatalogActivity mCatalogActivity; private ArrayList 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() { diff --git a/app/src/main/java/xyz/fycz/myreader/ui/read/catalog/CatalogPresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/read/catalog/CatalogPresenter.java index a5fc8ef..d950b31 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/read/catalog/CatalogPresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/read/catalog/CatalogPresenter.java @@ -31,7 +31,7 @@ public class CatalogPresenter implements BasePresenter { public CatalogPresenter(CatalogFragment mCatalogFragment) { this.mCatalogFragment = mCatalogFragment; - mChapterService = new ChapterService(); + mChapterService = ChapterService.getInstance(); } @Override diff --git a/app/src/main/java/xyz/fycz/myreader/ui/search/SearchBookPrensenter.java b/app/src/main/java/xyz/fycz/myreader/ui/search/SearchBookPrensenter.java index 21f9f94..4e18fe4 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/search/SearchBookPrensenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/search/SearchBookPrensenter.java @@ -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]); } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/user/LoginActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/user/LoginActivity.java index 3031ead..ba5648a 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/user/LoginActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/user/LoginActivity.java @@ -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() { diff --git a/app/src/main/java/xyz/fycz/myreader/ui/user/LoginPresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/user/LoginPresenter.java index 946e210..2b67529 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/user/LoginPresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/user/LoginPresenter.java @@ -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 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 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() { diff --git a/app/src/main/java/xyz/fycz/myreader/ui/user/RegisterActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/user/RegisterActivity.java index 4153897..1744e7b 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/user/RegisterActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/user/RegisterActivity.java @@ -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() { diff --git a/app/src/main/java/xyz/fycz/myreader/ui/user/RegisterPresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/user/RegisterPresenter.java index 3829899..b80af55 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/user/RegisterPresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/user/RegisterPresenter.java @@ -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 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(); } }); } diff --git a/app/src/main/java/xyz/fycz/myreader/util/FileStack.java b/app/src/main/java/xyz/fycz/myreader/util/FileStack.java new file mode 100644 index 0000000..b8f5691 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/util/FileStack.java @@ -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 files; + public int scrollOffset; + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/util/ImageUtil.java b/app/src/main/java/xyz/fycz/myreader/util/ImageUtil.java new file mode 100644 index 0000000..89b22c3 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/util/ImageUtil.java @@ -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); + } + } + +} diff --git a/app/src/main/java/xyz/fycz/myreader/util/StatusBarCompat.java b/app/src/main/java/xyz/fycz/myreader/util/StatusBarCompat.java new file mode 100644 index 0000000..322acf1 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/util/StatusBarCompat.java @@ -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; + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/util/media/LoaderCreator.java b/app/src/main/java/xyz/fycz/myreader/util/media/LoaderCreator.java new file mode 100644 index 0000000..5168495 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/util/media/LoaderCreator.java @@ -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!"); + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/util/media/LocalFileLoader.java b/app/src/main/java/xyz/fycz/myreader/util/media/LocalFileLoader.java new file mode 100644 index 0000000..6a9e357 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/util/media/LocalFileLoader.java @@ -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 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; + } + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/util/media/MediaStoreHelper.java b/app/src/main/java/xyz/fycz/myreader/util/media/MediaStoreHelper.java new file mode 100644 index 0000000..d353e12 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/util/media/MediaStoreHelper.java @@ -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 { + + /** + * 获取媒体库中所有的书籍文件 + *

+ * 暂时只支持 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 files); + } + + /** + * Loader 回调处理 + */ + static class MediaLoaderCallbacks implements LoaderManager.LoaderCallbacks { + protected WeakReference mContext; + protected MediaResultCallback mResultCallback; + + public MediaLoaderCallbacks(Context context, MediaResultCallback resultCallback) { + mContext = new WeakReference<>(context); + mResultCallback = resultCallback; + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + return LoaderCreator.create(mContext.get(), id, args); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + LocalFileLoader localFileLoader = (LocalFileLoader) loader; + localFileLoader.parseData(data, mResultCallback); + } + + @Override + public void onLoaderReset(Loader loader) { + } + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/widget/DividerItemDecoration.java b/app/src/main/java/xyz/fycz/myreader/widget/DividerItemDecoration.java new file mode 100644 index 0000000..6cf30c3 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/widget/DividerItemDecoration.java @@ -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); + } + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/widget/RefreshLayout.java b/app/src/main/java/xyz/fycz/myreader/widget/RefreshLayout.java new file mode 100644 index 0000000..4faad4d --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/widget/RefreshLayout.java @@ -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 CREATOR + = new Creator() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + //添加错误重新加载的监听 + public interface OnReloadingListener{ + void onReload(); + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/widget/page/PageView.java b/app/src/main/java/xyz/fycz/myreader/widget/page/PageView.java index ceaf428..83056d9 100644 --- a/app/src/main/java/xyz/fycz/myreader/widget/page/PageView.java +++ b/app/src/main/java/xyz/fycz/myreader/widget/page/PageView.java @@ -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) { diff --git a/app/src/main/res/color/selector_btn_file_add.xml b/app/src/main/res/color/selector_btn_file_add.xml new file mode 100644 index 0000000..ba3485d --- /dev/null +++ b/app/src/main/res/color/selector_btn_file_add.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/selector_btn_file_delete.xml b/app/src/main/res/color/selector_btn_file_delete.xml new file mode 100644 index 0000000..6ef72c8 --- /dev/null +++ b/app/src/main/res/color/selector_btn_file_delete.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/selector_cb_file.xml b/app/src/main/res/color/selector_cb_file.xml new file mode 100644 index 0000000..acfdd26 --- /dev/null +++ b/app/src/main/res/color/selector_cb_file.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/ic_back_last.png b/app/src/main/res/drawable-xhdpi/ic_back_last.png new file mode 100644 index 0000000000000000000000000000000000000000..0fede2323345fd6a1ba199d0a93f0512afc78d78 GIT binary patch literal 616 zcmV-u0+;=XP)vz`c_4fPy{#B$7HjHh+Ik!bbUxW}()^#VM zlf`0jFdmO9wz>!++ki3V2mmI59>+?l2>{d)T38U40c-6WA;jyLjzsicYyC0?%fYlZ z;GCNf(NW80uMqKFOHLHLl>uw5-*>Yp0Nt7Aw?Om);w?)xuUP}-Xfy^a1gw*8Cac0MDz>*9t1mY0Qipvx1Kxy1l?a=CaVfn1a1)+q^aCzp#?638`4Zk<0a3DhxVp~-;&0000U!prw3eNZ(C`C^{Wz`*#?)5S5Q;?~<)H}jee1Y8Y&hlxFQc<|qT3-d|u z#b&JDJmIHo`wuRTIa?u?)6^i~#G%-NOq^()VbC?%W z-`kseUhZoT&(tcU7K> zUrkGYPq$vFVZODl%Ea%r-i242FTFmVEGBfrE7MKLUgx-vnBm2l3#9sGRd#O)^bB3O z>T}@Y$gFK{ho-fc-zmO6)#{~z?Dkn9L5Dm-Y?(qlV$Q$+AGFy@ez)J^c31XYue{rP zP100fC#RdLEkCkZ*z>%%pSZej&4-QBDG?U2F=5xw9Y1%qv}~8fN|iX(gC4p+a|$mc zw)%0r2x5wDS$JE#%_eIn1JS@ z0Xfda7M`19R@!(^w%~d?Lo{{iiuP5foLX)zlbf;k&#T_@t+}pltJdCU&OPDv_+!E1 zIcg`AdifTHbym^36IZj;TdeaB-_8B@+x`2d-Yc}qw}1PvHMn)fpWWHF*TrgT z%KW>ttBglca8+d9+mFwS*S)^}KVzk|J8!r6?m(R-;g@zAmiYZuE4Dw}d+F-aP4DJg z%bt6eH)XBf^|#afzWWrK?e6`x?CR5y^SP4Ygw9kTk>1B zmSg809TO1P8)CF+^G?Ghlj?-zZbaUdnDk*m&DS??@_3$RNBpoZ6VBslt-oSa`)1i4 ziKu0Tn}2Sr-gS4+z5T~>f1S=d)VtL8dQ8E-GBNXM+ljlH_wAOPESdq5^mO%e JS?83{1OVP$QQ!ao literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_file_loaded.png b/app/src/main/res/drawable-xhdpi/ic_file_loaded.png new file mode 100644 index 0000000000000000000000000000000000000000..30193ee50dad7a88055a3d3c41d0f81ccc313fd5 GIT binary patch literal 1007 zcmeAS@N?(olHy`uVBq!ia0vp^b3mAb8A$F;GpYwt6#+gWu0ZLkJpJD|E|2u5{b9rK#q7b|EvINhSY9)J2?#^J%4z@Yd-B5f-Yc2x= z(?3ra$B>F!Z)d$OTI3+m%DD4XLqmhYL$$bj|9Q6`JdzqSEn$_+GOg#omzQWApEq+K zZx<8LCRC8%8j`zMdRgl2X_+sieW$w5++3P_EOB?VJ6Gns{mH^_YVvC{-^F?hxZSh! zn^f`MHL>9Tx!CZysvWCO%oDqK^4%={msPilR)-hpESo2K{NK!}>zBS=d_O1k>;B`R zH}m)Qhb~dI`uo1>nsKGCTGb|}KL)?fik+7}`uCb~i~H0i*RO54KXa?^dcQ62GknXJ ztG21%nz8G_W(Ph?U1QscD=!33oNPU7>9H-FtQ!SYm;QLR&3kRMmFTn`89tQ@`=-x6 zE)vLh=#%E#MbocoF1Ht$nw~k+OK(j`rAX<$bc^ouvbC#rOzJCY)jn6)Kj&)J!>6lL zjQAsMR9Y9NaLxK7cwZ}V-Dl0~_H)EOgw9Frv%lJZ$04*qUnPIy;;xP2Cq3&f%k(ak zDw|nrY1S*x^X`qDDOY6YiKN>f|6M#`8!zh|J+Xe>x%7^}z1+z+E9U1Nm1)n>S@-1A zv8ksE>UoV7-JO-N!H3GR9&y_6i~MPXh41gAfX z`qjI`!{up8!NU@P?UGHiOXrHZJ07ptZ6UPj`O%{)Yjj@zPV&1m&*;SMqk&sR>?_~d zsBAto`)ka9;d^?0N}iu4GfxcAiS(Sa%1bdXEAs2Cx37}-{oCXx!*S|k+5E5HSIc*- z4EPkd#j9|a>gx7MCl&TQaNyuP6trA_rSSeMs+VIn&Me;d=!k35ul3K3Y&m!@Z(IAI z;Ps_F#k+dzHs;*kV{(iu)$00#4F9v!kJg`aF}H}xUa>LK=;B(%>mk-+tJg=ZQuCV{ z>%Duw9OvTAlT}uhbI*w7$X~Uy(4|W5{N!CZHb>Tcdvsi|h`(rwT;oYwn<-Uqw7wsR zdKb9=&UBTac>R;LhRZ!_m##cfE2;C{beGasZjNS~%J{nyVuAOj$Gx+ixn(^!BWj{$ cU^tNfgugx7tLewrmAxQ2Pgg&ebxsLQ0K~oX6aWAK literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_no_data.png b/app/src/main/res/drawable-xhdpi/ic_no_data.png new file mode 100644 index 0000000000000000000000000000000000000000..2578fbe396da87239cc4a35f0bb1cfa67310e9f0 GIT binary patch literal 5615 zcmaJ_c{r4N`?rJ=<|tWZ zrLvQy6h$#a(vT4S#(B>99f@Hx^2hsPfEGA(AjTep_CFu-9HYy|#wmk! zbRoK+tl)lloC^9tMumh6XKAys=^Bv2(S9KqBG4BThz-+|S!-&O0b>32WSn6rRaCeM zCJ1XD8;Nm>XP6r0vv{j29j{0VFWlyPv#F^IBS1!4VD4^Awmq% zllhlZE+|`|2|f}7gsG@2`>8@8Kn)EQ2o$EF0Z{^~sY2Aisw`+IL!fY&23$=O_~#?T zvKHwd0Jle&{%MPK(vt}y62swOa7;{$N{qS+J~9vt(bm>J;80UjW=SX$&W91vB;_yy z=r0BYhTs>84JTspVZZ}Mv@br2s3*hn^j{(1!vCfXBm9{rR>Hs}bT}BIqIwY0UxFyq z{}#pJ{+1>X?J@uM_rDSo9M6Yi!1fpdJ}T0WRk#4qfvIq~NhAhM#78>f@u7cZ(KZNA z#1n$>;Xo4y4WNQEHq0L%Lpc41JPHN32qO^DVSX43gq{qGK?RHThns4tLp9Wlj9^Bl z5C{aRVXS3hs-Sw>(YvC$ZR z(?~oH_(#Za>_7XW^^bgi>iYk)FWUd83uZY3J}B&e6#B0#R`(oi|885>;qS)Bgt59k zlGWDh=%6Atwqt%42qQ<*>y?6--_MPo=ox~Va!Ht*bsZihA~H-L3JKadBDzIyOc`c) z-p)cKnO?dzT9W*$aA+-(X$E!eLHs6q7D@++L%Dz9GO~R_l-uolh5v{l_cyJ*{rb6O zakpx-Dlai_H%xo>?e4~dCr{uehtmopXE=<5^bc@&!0Z6U+Xs+_N*y3&0d#<2@}KyH zcJ%*!@=0q$;nS3TU9`WLlM1gi

LD*sTPstiQK(64$EvnA z%Z*Dxr3{G?dAc^8OQD2*Y3E`D=j0TUc$aa(Ei4?RL!~hueYUI7RUXe7P=ic`wpe@Y z`kuZY;zCS6&1~{$Hpv1XA8<<(u!S9u`eoCWc}{5Ec)Bb}%=<)((oRcIRuJJxjfmjA z7KW^yl$}k>zVDREYsDS=jcnSntaX0a2lxPA{uMH`NYm$W(ell9`3e*o+lS znwRn5aLuC((}sQe%nsq);i89jbsnHSiQpGpJA1LTs|zN{FYV~X^j5hY?z{Z-7^sZ? zuRFl5AWF{jOLQjensd&(c+jahQ>Yy@XMr$DM%4jl?%{vhC!5mbk=J3kqt`yuC6Ix^ zdetw<3Qrt1^2_Ecpi)iZk;y%mmguz*h?m?c#PV)=kfCAL5!2mbMnv4m%E{{?t8%om zR2Mv#w{`Y~F~v&&avh1}GyYcYJlBR5_fjzEXj z1cqZnnJM};$wAYBy&rsUEepnyD}Wi2g`4)TY%*fZ zd22+J>lyK{t}mSBMr|gp7L0y+s^0Qu%9^>Cv1-jIm|J{hQ`2y5fvSn=`8Kojh{-FW1sEgA`3i&1|aH zoJPs(^Pzrs4rLLOLKo!eenG9IM6|_Z-iLHA>Fz{!p0-~em2S_mn=bq5d&OiAyiDeP zG~-rNQ?hLdaMQ3E<1u-o`VL8-$F`1sJ>@6=^mB*Wl2Fw~W5bZA z-F%*SDP0(Js0P1Rqk-YWd0tN+n5Cauw?{FNJlUdC86Ptbd(OKFj|S^oW#c*`&C(0T zXVVs_sk{TsM$Y5x0~;7CCX)EjbrvM1xQX#FVqOAdpEICfGW`?ntOu~IUpm~YA$=0h z>6g=n3MDEuyulM&=6d%h0(ofQA2-u?B<}#Sb~alJBhH9m)up%{8vI>Bn+jvm3Bfe5 zuIj}aSfBWFb|>i6rG=wg+|Cd4vrdLW-s37z%p*~X#>)>;b&#+VBhXr-y3=7xTu1gU zHO)-5Olx9Ol1C=)zEW-Gd+4#S+%`s*uC=TilqJsdjRL2~{qbqS%Y@WdtrJBe)NJZR z`BHs3r0<;jeD;ab0rD9{B`Q|aC?ab+xY~fSUuIC_Ncfc|n_FW!^w7QyUxHWr;ZfzS zS@8n42YilcX+;F4d5N@~-npYc>nP~VM5`%j5W5N^S78}l!ClIZsy=zNDl3O;tF-6` zmC|`+xmb%0I%AEFal34z908HCO8K=Fvc^j-ki@N|Od0F+Pv7U0&n)c;Th}$&$V_v+ zZB84woL%19Be2);t@SPc?ve+46jKDT6&%~x{Yb)Ak$EnJ*knq)_nuo$CGp9$H)4wt zAU++uBx`pQa9q4LOV8<=q|f7*$suJoK{S=v*0M5gM`#(5lbHImV=2nTkh{i>vCUxD38I1^Sef`?)UgE{3@)P({LNhMZU`9mxR9k^X zdyvAqVP3gw$-;F&XmyWH?#E&6R%pG{*3;iIujZ)$sPRGsPG6a=2>RtNKpeWu_)`e7 z(X%x_aK%G+PMCpc8<`SYL?__#;i$>KRS}S*+cs?ndS}6pY&QnYCPMn#ETDGyRK{%}K#@z` zuQ^Qe=OON9Ymm=%%38;|r1vo~I5tH$!mQ2KwLPQrlJ{M0tqAHC;wmj6dvh!(yt!Aw zzKFN#rx@)0>%+?=3h6kkR4K*k5Ta{;;_;-hjJIYKAtW$EUFCvW5I5P1INFB$vH7+mVKUW&XQi?t zAW*8;uW^RW>y98rDVs5Yyn#Z-Szcq%f)Nt9;-QkM9uTW?RXj;gcthe=tO8@XUyb zQ<*>Et6yjN$lsNiIhmXpyU9oKn=KI>zREswn1C_lMx<(v1a>8_7miUH%8S3yz#rcr z&rtK#J_wgr`nYc0Z?N|&<0d}~$1L(y@yftk1Xm>e*L@ zkV24zFFBr}(k}bD38eN;112dmGYSOc50Rw~EfKHf)~SEq{S;Tloi7;~Zn*b#1n4ke z+}|)f6%aTlP$b(<7UL8EKse?kbpuwG-xRNyWz}?GZnPdr)9Ke-_n^g@eLNl)9hA~R zZoO63f^(Wj=VODIVoap|ho3(K&J$DNaeeaG>wQ+I!YvaL=ay8LNvz*P4*bFyY3$)6 zsj>oevrnP8goR29z5haEzf_Jkad6wiEiCGWHHS+stCu-6N)DyffL>4nSL*c7Z--uCZXC^FCVHJ^?L(kMTY zmpN=`jN*Jn*p>7#6z20uVPLCxV$XaB>pRx~KOf!2B!oq=`-h8h)?ax+b`cm@iwu($ z^J(1t2BDG`C8n<~_L6cHql#*&a@(uvy`kSv$AfOkGc8*1l{9>r#l$ek-lL%AVVW6L zUPiKBT545&G?bO`XH(&@cbfWfgtA%*?|Ol8ahWp$NGTBc~)mlIbx` z>kSkUnVFX0CpW}5dn%GB2@X`4Pw|BZev`DO%_Q?!qYu*!DMeQq7tm}Katk}?iCx_0 zYo+_v1G#K`l=yb`;P(1rn$4JvM}MDEQ3baHVdr<~^b6lh z_Mix@6k^ zRmR}(F40Ckbk9oN6X_{aJ4^lZv?>B4q8X>3!glJ~n#Zdh{w(FoCw^=;QE&b8rxY&eObMI zx1wIRqP`HLvg3^S_LaF>_O?sr*yk_3;XLPUD<1S`ft?vgn{uC})TiojSi)M!gESe` z<$K|DkF^EW0UmwzXazvn@`cpo`(qs6uwB0wzDVHzUQKQ+Gvo}TJR`m0&MzrysBdpG ziHlD1SS-80Oy2C^K{MM48)wFbKO{#+dJT`*B(m3h4Qfvp{0tqj{yZoeU(!3YCwFEJ z7tAP2?bdCnFi9@}m|x>x_+IP|Os_C?`SLAzWJQ-NmVQ-8RgtrLYz1#$?WnHEOuVsE z)@k_4HI=$1r?-O^m_|#V`l*L93S4U^1;w2_VK{$TYPcvD>*>jAjGHfQyQnY7rk~;- z!f=g!;ZFx-A2!rVAT$6=-yhMGTkLB_IC>;JedjS|?G@te&XZKqlumqPD@CMqTTN*q zJY6|%@%~Bq?09;yT(cN}0OJ^Zp*B;VD++c#aZ{EEO4z!) zyRQ_{=Q5+T;o>^4-`m;&=JDl%X#&LG0NET{Zj_IA`n%eic-cdn1Rg;xAl`A&BIBYu z8C_EhSgS^Ryh+~4{@C^Q zdxX~V#qF}e-$*aFo0H_GuR2o548P9)Wrw=X+3c$-!a!w&=4h|4AyI)~h~(|h;a7ZE zRPLo3o%t$}Osw^7v{=ZjFO4H?SFwgJ*Sr`1VU}vfZ;tPjR#wZ)8r-&ef2=O{elmFUY4bUQQ?C7%(L$ZPmxcBz ypG?{!q3hOt5lnfCEk2rI7}W!&%sNy&%S|HgZGLrS>0zMz7(8A5T-G@yGywpV&r;<8 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/ic_book_has.xml b/app/src/main/res/drawable/ic_book_has.xml new file mode 100644 index 0000000..a920e50 --- /dev/null +++ b/app/src/main/res/drawable/ic_book_has.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_folder.xml b/app/src/main/res/drawable/ic_folder.xml new file mode 100644 index 0000000..cb2a84c --- /dev/null +++ b/app/src/main/res/drawable/ic_folder.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_tag_txt.xml b/app/src/main/res/drawable/ic_tag_txt.xml new file mode 100644 index 0000000..1cfc74d --- /dev/null +++ b/app/src/main/res/drawable/ic_tag_txt.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_btn_add.xml b/app/src/main/res/drawable/selector_btn_add.xml new file mode 100644 index 0000000..d0b5df1 --- /dev/null +++ b/app/src/main/res/drawable/selector_btn_add.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_btn_delete.xml b/app/src/main/res/drawable/selector_btn_delete.xml new file mode 100644 index 0000000..bdf86fb --- /dev/null +++ b/app/src/main/res/drawable/selector_btn_delete.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_cb_file.xml b/app/src/main/res/drawable/selector_cb_file.xml new file mode 100644 index 0000000..17deb8d --- /dev/null +++ b/app/src/main/res/drawable/selector_cb_file.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_common_bg.xml b/app/src/main/res/drawable/selector_common_bg.xml new file mode 100644 index 0000000..701a4c2 --- /dev/null +++ b/app/src/main/res/drawable/selector_common_bg.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_pwd.xml b/app/src/main/res/drawable/selector_pwd.xml new file mode 100644 index 0000000..f3b6764 --- /dev/null +++ b/app/src/main/res/drawable/selector_pwd.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_blue.xml b/app/src/main/res/drawable/shape_blue.xml new file mode 100644 index 0000000..a61553f --- /dev/null +++ b/app/src/main/res/drawable/shape_blue.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_corner_black.xml b/app/src/main/res/drawable/shape_corner_black.xml new file mode 100644 index 0000000..44b77df --- /dev/null +++ b/app/src/main/res/drawable/shape_corner_black.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_corner_blue.xml b/app/src/main/res/drawable/shape_corner_blue.xml new file mode 100644 index 0000000..ecf07cf --- /dev/null +++ b/app/src/main/res/drawable/shape_corner_blue.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_unclick.xml b/app/src/main/res/drawable/shape_unclick.xml new file mode 100644 index 0000000..3cca521 --- /dev/null +++ b/app/src/main/res/drawable/shape_unclick.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_book_info.xml b/app/src/main/res/layout/activity_book_info.xml index add0a94..20811a5 100644 --- a/app/src/main/res/layout/activity_book_info.xml +++ b/app/src/main/res/layout/activity_book_info.xml @@ -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"/> diff --git a/app/src/main/res/layout/activity_file_system.xml b/app/src/main/res/layout/activity_file_system.xml new file mode 100644 index 0000000..46f0b49 --- /dev/null +++ b/app/src/main/res/layout/activity_file_system.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + +