优化书籍详情

pull/5/head
fengyuecanzhu 4 years ago
parent 5f73042209
commit f2a98582f1
  1. BIN
      .idea/caches/build_file_checksums.ser
  2. 3
      app/build.gradle
  3. 10
      app/src/main/AndroidManifest.xml
  4. 6
      app/src/main/assets/updatelog.fy
  5. 29
      app/src/main/java/xyz/fycz/myreader/application/MyApplication.java
  6. 15
      app/src/main/java/xyz/fycz/myreader/backup/BackupAndRestore.java
  7. 1
      app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java
  8. 4
      app/src/main/java/xyz/fycz/myreader/base/adapter/BaseListAdapter.java
  9. 2
      app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
  10. 353
      app/src/main/java/xyz/fycz/myreader/ui/bookinfo/BookDetailedActivity.java
  11. 4
      app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemActivity.java
  12. 2
      app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemAdapter.java
  13. 4
      app/src/main/java/xyz/fycz/myreader/ui/filesys/LocalBookFragment.java
  14. 9
      app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseAdapter.java
  15. 3
      app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseDetailedAdapter.java
  16. 1
      app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseFragment.java
  17. 259
      app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcasePresenter.java
  18. 3
      app/src/main/java/xyz/fycz/myreader/ui/home/bookstore/BookStorePresenter.java
  19. 151
      app/src/main/java/xyz/fycz/myreader/ui/read/ReadPresenter.java
  20. 3
      app/src/main/java/xyz/fycz/myreader/ui/search/SearchBookPrensenter.java
  21. 6
      app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java
  22. 14
      app/src/main/java/xyz/fycz/myreader/util/TextHelper.java
  23. 23
      app/src/main/java/xyz/fycz/myreader/util/notification/NotificationClickReceiver.java
  24. 171
      app/src/main/java/xyz/fycz/myreader/util/notification/NotificationUtil.java
  25. 12
      app/src/main/res/drawable/ic_vector_add_bookcase.xml
  26. 9
      app/src/main/res/drawable/ic_vector_book_read.xml
  27. 6
      app/src/main/res/drawable/selector_pwd.xml
  28. 79
      app/src/main/res/layout/activity_book_detail.xml
  29. 43
      app/src/main/res/layout/layout_book_detail_bottom.xml
  30. 98
      app/src/main/res/layout/layout_book_detail_content.xml
  31. 98
      app/src/main/res/layout/layout_book_detail_header.xml
  32. 19
      app/src/main/res/menu/menu_book_detail.xml
  33. BIN
      app/src/main/res/mipmap-xhdpi/ic_menu_exchange.png
  34. BIN
      app/src/main/res/mipmap-xhdpi/ic_menu_refresh.png
  35. BIN
      app/src/main/res/mipmap-xhdpi/pwd_gone.png
  36. BIN
      app/src/main/res/mipmap-xhdpi/pwd_visiable.png
  37. 4
      app/src/main/res/values/strings.xml
  38. 37
      app/src/main/res/values/styles.xml
  39. 4
      app/version_code.properties

@ -115,7 +115,8 @@ dependencies {
implementation 'com.google.android.material:material:1.1.0' implementation 'com.google.android.material:material:1.1.0'
implementation 'com.h6ah4i.android.widget.verticalseekbar:verticalseekbar:1.0.0' implementation 'com.h6ah4i.android.widget.verticalseekbar:verticalseekbar:1.0.0'
//Scroller
implementation 'com.futuremind.recyclerfastscroll:fastscroll:0.2.5'
} }
greendao { greendao {

@ -54,6 +54,7 @@
android:roundIcon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppDayTheme" android:theme="@style/AppDayTheme"
android:requestLegacyExternalStorage="true"
android:networkSecurityConfig="@xml/network_security_config"> android:networkSecurityConfig="@xml/network_security_config">
<activity android:name=".ui.home.Splash"> <activity android:name=".ui.home.Splash">
@ -74,11 +75,13 @@
android:resource="@xml/file_paths"/> android:resource="@xml/file_paths"/>
</provider> </provider>
<activity android:name=".ui.home.MainActivity"/> <activity android:name=".ui.home.MainActivity"
android:launchMode="singleTask"/>
<activity <activity
android:name=".ui.search.SearchBookActivity" android:name=".ui.search.SearchBookActivity"
android:windowSoftInputMode="stateVisible"/> android:windowSoftInputMode="stateVisible"/>
<activity android:name=".ui.bookinfo.BookInfoActivity"/> <activity android:name=".ui.bookinfo.BookInfoActivity"/>
<activity android:name=".ui.bookinfo.BookDetailedActivity"/>
<activity <activity
android:name=".ui.read.ReadActivity" android:name=".ui.read.ReadActivity"
@ -101,6 +104,11 @@
<activity android:name=".ui.filesys.FileSystemActivity"/> <activity android:name=".ui.filesys.FileSystemActivity"/>
<receiver android:name=".util.notification.NotificationClickReceiver"/>
<receiver android:name=".ui.home.bookcase.BookcasePresenter$cancelDownloadReceiver"/>
<receiver android:name=".ui.read.ReadPresenter$cancelDownloadReceiver"/>
</application> </application>
</manifest> </manifest>

@ -1,3 +1,9 @@
2020.08.15
风月读书v1.20.081523
1、修复安卓10(Q)无法访问外部储存空间及其导致的一系列问题(如:无法下载字体、无法使用备份\恢复、无法添加本地书籍、无法同步书架等)
2、修复一键缓存的bug
3、新增缓存时在通知栏提示缓存进度,并可在通知栏停止缓存
2020.08.12 2020.08.12
风月读书v1.20.081221 风月读书v1.20.081221
1、新增本地书籍导入页面(不再使用系统文件管理器导入),支持一次性导入多本书籍 1、新增本地书籍导入页面(不再使用系统文件管理器导入),支持一次性导入多本书籍

@ -13,7 +13,9 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.os.Handler; import android.os.Handler;
@ -61,6 +63,9 @@ public class MyApplication extends Application {
application = this; application = this;
HttpUtil.trustAllHosts();//信任所有证书 HttpUtil.trustAllHosts();//信任所有证书
// handleSSLHandshake(); // handleSSLHandshake();
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel();
}
mFixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());//初始化线程池 mFixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());//初始化线程池
BaseActivity.setCloseAntiHijacking(true); BaseActivity.setCloseAntiHijacking(true);
@ -120,10 +125,12 @@ public class MyApplication extends Application {
@TargetApi(26) @TargetApi(26)
private void createNotificationChannel() { private void createNotificationChannel() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(APPCONST.channelIdDownload, "下载通知", NotificationManager.IMPORTANCE_LOW);
notificationManager.createNotificationChannel(new NotificationChannel("gxdw_push_and_im", "gxdw", NotificationManager.IMPORTANCE_DEFAULT)); channel.enableLights(true);//是否在桌面icon右上角展示小红点
channel.setLightColor(Color.RED);//小红点颜色
channel.setShowBadge(false); //是否在久按桌面图标时显示此渠道的通知
notificationManager.createNotificationChannel(channel);
} }
@ -246,12 +253,12 @@ public class MyApplication extends Application {
Document doc = null; Document doc = null;
try { try {
String url = "https://shimo.im/docs/cqkgjPRRydYYhQKt/read"; String url = "https://shimo.im/docs/cqkgjPRRydYYhQKt/read";
if (isApkInDebug(getmContext())){ if (isApkInDebug(getmContext())) {
url = "https://shimo.im/docs/zfzpda7MUGskOC9v/read"; url = "https://shimo.im/docs/zfzpda7MUGskOC9v/read";
} }
doc = Jsoup.connect(url).get(); doc = Jsoup.connect(url).get();
String content = doc.getElementsByClass("ql-editor").text(); String content = doc.getElementsByClass("ql-editor").text();
if (StringHelper.isEmpty(content)){ if (StringHelper.isEmpty(content)) {
TextHelper.showText("检查更新失败!"); TextHelper.showText("检查更新失败!");
return; return;
} }
@ -267,9 +274,9 @@ public class MyApplication extends Application {
updateContent = contents[3].substring(contents[3].indexOf(":") + 1); updateContent = contents[3].substring(contents[3].indexOf(":") + 1);
SharedPreUtils spu = SharedPreUtils.getInstance(); SharedPreUtils spu = SharedPreUtils.getInstance();
spu.putString("lanzousKeyStart", contents[4].substring(contents[4].indexOf(":") + 1)); spu.putString("lanzousKeyStart", contents[4].substring(contents[4].indexOf(":") + 1));
if (!StringHelper.isEmpty(downloadLink)){ if (!StringHelper.isEmpty(downloadLink)) {
spu.putString("downloadLink", downloadLink); spu.putString("downloadLink", downloadLink);
}else { } else {
spu.putString("downloadLink", URLCONST.APP_DIR_UR); spu.putString("downloadLink", URLCONST.APP_DIR_UR);
} }
String[] updateContents = updateContent.split("/"); String[] updateContents = updateContent.split("/");
@ -307,12 +314,12 @@ public class MyApplication extends Application {
final boolean isForceUpdate, final BookcaseFragment mBookcaseFragment) { final boolean isForceUpdate, final BookcaseFragment mBookcaseFragment) {
//String version = (versionCode / 100 % 10) + "." + (versionCode / 10 % 10) + "." + (versionCode % 10); //String version = (versionCode / 100 % 10) + "." + (versionCode / 10 % 10) + "." + (versionCode % 10);
String cancelTitle; String cancelTitle;
if(isForceUpdate){ if (isForceUpdate) {
cancelTitle = "退出"; cancelTitle = "退出";
}else { } else {
cancelTitle = "忽略此版本"; cancelTitle = "忽略此版本";
} }
if (mBookcaseFragment == null){ if (mBookcaseFragment == null) {
DialogCreator.createCommonDialog(activity, "发现新版本:", message, true, "取消", "立即更新", null, DialogCreator.createCommonDialog(activity, "发现新版本:", message, true, "取消", "立即更新", null,
(dialog, which) -> goDownload(activity, url)); (dialog, which) -> goDownload(activity, url));
return; return;
@ -345,7 +352,7 @@ public class MyApplication extends Application {
}); });
} }
private void goDownload(Activity activity, String url){ private void goDownload(Activity activity, String url) {
String downloadLink = url; String downloadLink = url;
if (url == null || "".equals(url)) { if (url == null || "".equals(url)) {
downloadLink = URLCONST.APP_DIR_UR; downloadLink = URLCONST.APP_DIR_UR;

@ -48,20 +48,7 @@ public class BackupAndRestore {
e.printStackTrace(); e.printStackTrace();
return false; return false;
} finally { } finally {
if (bw != null) { IOUtils.close(bw, oos);
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} }
} }

@ -126,5 +126,4 @@ public class BaseActivity extends AppCompatActivity {
return mInputMethodManager; return mInputMethodManager;
} }
} }

@ -1,9 +1,11 @@
package xyz.fycz.myreader.base; package xyz.fycz.myreader.base.adapter;
import android.os.Handler; import android.os.Handler;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import xyz.fycz.myreader.base.BaseViewHolder;
import xyz.fycz.myreader.base.IViewHolder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;

@ -71,4 +71,6 @@ public class APPCONST {
public static final int SETTING_VERSION = 1; public static final int SETTING_VERSION = 1;
public static final String FORMAT_FILE_DATE = "yyyy-MM-dd"; public static final String FORMAT_FILE_DATE = "yyyy-MM-dd";
public final static String channelIdDownload = "channel_download";
} }

@ -0,0 +1,353 @@
package xyz.fycz.myreader.ui.bookinfo;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.crawler.BookInfoCrawler;
import xyz.fycz.myreader.crawler.ReadCrawler;
import xyz.fycz.myreader.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.creator.ChangeSourceDialog;
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.ui.read.ReadActivity;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.TextHelper;
import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.webapi.CommonApi;
import java.util.ArrayList;
/**
* @author fengyue
* @date 2020/8/17 11:39
*/
public class BookDetailedActivity extends BaseActivity2 {
@BindView(R.id.book_detail_iv_cover)
ImageView mIvCover;
@BindView(R.id.book_detail_tv_author)
TextView mTvAuthor;
@BindView(R.id.book_detail_tv_type)
TextView mTvType;
@BindView(R.id.book_detail_newest_chapter)
TextView mTvNewestChapter;
@BindView(R.id.book_detail_source)
TextView mTvSource;
@BindView(R.id.book_detail_tv_add)
TextView bookDetailTvAdd;
@BindView(R.id.book_detail_tv_open)
TextView bookDetailTvOpen;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.book_detail_tv_desc)
TextView mTvDesc;
@BindView(R.id.tv_disclaimer)
TextView mTvDisclaimer;
@BindView(R.id.fl_add_bookcase)
FrameLayout flAddBookcase;
@BindView(R.id.fl_open_book)
FrameLayout flOpenBook;
@BindView(R.id.book_detail_rv_catalog)
RecyclerView bookDetailRvCatalog;
@BindView(R.id.pb_loading)
ProgressBar pbLoading;
private Book mBook;
private ArrayList<Book> aBooks;
private BookService mBookService;
private ReadCrawler mReadCrawler;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
initBookInfo();
break;
case 2:
createChangeSourceDia();
break;
case 3:
pbLoading.setVisibility(View.GONE);
DialogCreator.createTipDialog(BookDetailedActivity.this, "未搜索到该书籍,书源加载失败!");
break;
case 4:
initOtherInfo();
break;
}
}
};
@Override
protected int getContentId() {
return R.layout.activity_book_detail;
}
@Override
protected void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
mBookService = BookService.getInstance();
aBooks = (ArrayList<Book>) getIntent().getSerializableExtra(APPCONST.SEARCH_BOOK_BEAN);
if (aBooks != null) {
mBook = aBooks.get(0);
} else {
mBook = (Book) getIntent().getSerializableExtra(APPCONST.BOOK);
}
mReadCrawler = ReadCrawlerUtil.getReadCrawler(mBook.getSource());
}
@Override
protected void setUpToolbar(Toolbar toolbar) {
super.setUpToolbar(toolbar);
getSupportActionBar().setTitle(mBook.getName());
}
@Override
protected void initWidget() {
super.initWidget();
initBookInfo();
mTvDisclaimer.setOnClickListener(v -> DialogCreator.createAssetTipDialog(this, "免责声明", "disclaimer.fy"));
if (isBookCollected()) {
bookDetailTvAdd.setText("移除书籍");
bookDetailTvOpen.setText("继续阅读");
}
}
@Override
protected void initClick() {
super.initClick();
flAddBookcase.setOnClickListener(view -> {
if (!isBookCollected()) {
mBookService.addBook(mBook);
TextHelper.showText("成功加入书架");
bookDetailTvAdd.setText("移除书籍");
} else {
mBookService.deleteBookById(mBook.getId());
TextHelper.showText("成功移除书籍");
bookDetailTvAdd.setText("加入书架");
bookDetailTvOpen.setText("开始阅读");
}
});
flOpenBook.setOnClickListener(view -> {
final boolean isCollected;
if (isBookCollected()) {
isCollected = true;
} else {
mBookService.addBook(mBook);
isCollected = false;
CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
mBookService.updateEntity(mBook);
}
@Override
public void onError(Exception e) {
}
});
}
Intent intent = new Intent(this, ReadActivity.class);
intent.putExtra(APPCONST.BOOK, mBook);
intent.putExtra("isCollected", isCollected);
startActivityForResult(intent, APPCONST.REQUEST_READ);
});
}
@Override
protected void processLogic() {
super.processLogic();
}
private boolean isBookCollected() {
Book book = mBookService.findBookByAuthorAndName(mBook.getName(), mBook.getAuthor());
if (book == null) {
return false;
} else {
mBook = book;
return true;
}
}
private void initBookInfo() {
mTvAuthor.setText(mBook.getAuthor());
if (StringHelper.isEmpty(mBook.getImgUrl())) {
mBook.setImgUrl("");
}
assert mBook.getNewestChapterTitle() != null;
mTvNewestChapter.setText("最新章节:" + mBook.getNewestChapterTitle().replace("最近更新 ", ""));
mTvDesc.setText("");
mTvType.setText("");
if (!"null".equals(mBook.getSource())) {
mTvSource.setText("书源:" + BookSource.fromString(mBook.getSource()).text);
}
ReadCrawler rc = ReadCrawlerUtil.getReadCrawler(mBook.getSource());
if (rc instanceof BookInfoCrawler && StringHelper.isEmpty(mBook.getImgUrl())) {
BookInfoCrawler bic = (BookInfoCrawler) rc;
CommonApi.getBookInfo(mBook, bic, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
mHandler.sendMessage(mHandler.obtainMessage(4));
}
@Override
public void onError(Exception e) {
}
});
} else {
initOtherInfo();
}
}
private void initOtherInfo() {
mTvDesc.setText(mBook.getDesc());
mTvType.setText(mBook.getType());
Glide.with(this)
.load(mBook.getImgUrl())
.error(R.mipmap.no_image)
.placeholder(R.mipmap.no_image)
//设置圆角
.apply(RequestOptions.bitmapTransform(new RoundedCorners(8)))
.into(mIvCover);
}
private void createChangeSourceDia() {
if (aBooks == null){
mHandler.sendMessage(mHandler.obtainMessage(3));
return;
}
pbLoading.setVisibility(View.GONE);
CharSequence[] sources = new CharSequence[aBooks.size()];
int checkedItem = 0;
for (int i = 0; i < sources.length; i++) {
sources[i] = BookSource.fromString(aBooks.get(i).getSource()).text
+ "\n" + aBooks.get(i).getNewestChapterTitle();
if (sources[i].equals(BookSource.fromString(mBook.getSource()).text
+ "\n" + aBooks.get(i).getNewestChapterTitle())) {
checkedItem = i;
}
}
final int finalCheckedItem = checkedItem;
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle("切换书源")
.setCancelable(true)
.setSingleChoiceItems(sources, checkedItem, (dialog1, which) -> {
boolean isBookCollected = isBookCollected();
if (finalCheckedItem == which) {
dialog1.dismiss();
return;
}
Book book = aBooks.get(which);
Book bookTem = new Book(mBook);
bookTem.setChapterUrl(book.getChapterUrl());
bookTem.setImgUrl(book.getImgUrl());
bookTem.setType(book.getType());
bookTem.setDesc(book.getDesc());
bookTem.setSource(book.getSource());
if (isBookCollected) {
mBookService.updateBook(mBook, bookTem);
}
mBook = bookTem;
mHandler.sendMessage(mHandler.obtainMessage(1));
if (isBookCollected) {
DialogCreator.createTipDialog(this,
"换源成功,由于不同书源的章节数量不一定相同,故换源后历史章节可能出错!");
}
dialog1.dismiss();
}).create();
dialog.show();
}
/********************************Event***************************************/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_book_detail, menu);
return true;
}
/**
* 导航栏菜单点击事件
*
* @param item
* @return
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_change_source: //换源
if (!NetworkUtils.isNetWorkAvailable()){
TextHelper.showText("无网络连接!");
return true;
}
pbLoading.setVisibility(View.VISIBLE);
if (aBooks == null) {
ChangeSourceDialog csd = new ChangeSourceDialog(this, mBook);
csd.init(new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
aBooks = (ArrayList<Book>) o;
mHandler.sendMessage(mHandler.obtainMessage(2));
}
@Override
public void onError(Exception e) {
mHandler.sendMessage(mHandler.obtainMessage(3));
}
});
} else {
createChangeSourceDia();
}
break;
case R.id.action_reload: //重新加载
initWidget();
processLogic();
break;
case R.id.action_open_link: //打开链接
Uri uri = Uri.parse(mBook.getChapterUrl());
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == APPCONST.REQUEST_READ) {
if (data == null) {
return;
}
boolean isCollected = data.getBooleanExtra(APPCONST.RESULT_IS_COLLECTED, false);
if (isCollected) {
bookDetailTvAdd.setText("移除书籍");
bookDetailTvOpen.setText("继续阅读");
}
}
}
}

@ -148,6 +148,10 @@ public class FileSystemActivity extends BaseTabActivity {
true, (dialog, which) -> { true, (dialog, which) -> {
//删除选中的文件 //删除选中的文件
mCurFragment.deleteCheckedFiles(); mCurFragment.deleteCheckedFiles();
//改变菜单状态
changeMenuStatus();
//改变是否可以全选
changeCheckedAllStatus();
//提示删除文件成功 //提示删除文件成功
TextHelper.showText("删除文件成功"); TextHelper.showText("删除文件成功");
}, null); }, null);

@ -1,7 +1,7 @@
package xyz.fycz.myreader.ui.filesys; package xyz.fycz.myreader.ui.filesys;
import xyz.fycz.myreader.base.BaseListAdapter; import xyz.fycz.myreader.base.adapter.BaseListAdapter;
import xyz.fycz.myreader.base.IViewHolder; import xyz.fycz.myreader.base.IViewHolder;
import xyz.fycz.myreader.greendao.service.BookService; import xyz.fycz.myreader.greendao.service.BookService;

@ -70,8 +70,12 @@ public class LocalBookFragment extends BaseFileFragment {
protected void processLogic() { protected void processLogic() {
super.processLogic(); super.processLogic();
//更新媒体库 //更新媒体库
try {
MediaScannerConnection.scanFile(getContext(), new String[]{Environment MediaScannerConnection.scanFile(getContext(), new String[]{Environment
.getExternalStorageDirectory().getAbsolutePath()}, new String[]{"text/plain"}, null); .getExternalStorageDirectory().getAbsolutePath()}, new String[]{"text/plain"}, null);
}catch (Exception e){
e.printStackTrace();
}
MediaStoreHelper.getAllBookFile(getActivity(), MediaStoreHelper.getAllBookFile(getActivity(),
(files) -> { (files) -> {
if (files.isEmpty()) { if (files.isEmpty()) {

@ -186,12 +186,7 @@ public abstract class BookcaseAdapter extends DragAdapter {
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
selectedIndex = which; selectedIndex = which;
} }
}).setNegativeButton("取消", (new DialogInterface.OnClickListener() { }).setNegativeButton("取消", ((dialog, which) -> dialog.dismiss())).setPositiveButton("确定",
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})).setPositiveButton("确定",
(dialog, which) -> { (dialog, which) -> {
switch (selectedIndex) { switch (selectedIndex) {
case 0: case 0:
@ -213,7 +208,7 @@ public abstract class BookcaseAdapter extends DragAdapter {
} }
Thread downloadThread = new Thread(() -> { Thread downloadThread = new Thread(() -> {
ArrayList<Chapter> chapters = (ArrayList<Chapter>) mChapterService.findBookAllChapterByBookId(book.getId()); ArrayList<Chapter> chapters = (ArrayList<Chapter>) mChapterService.findBookAllChapterByBookId(book.getId());
mBookcasePresenter.addDownload(book, chapters, begin[0], end[0]); mBookcasePresenter.addDownload(book, chapters, begin[0], end[0], false);
}); });
mBookcasePresenter.getEs().submit(downloadThread); mBookcasePresenter.getEs().submit(downloadThread);
}).show(); }).show();

@ -24,6 +24,7 @@ import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.common.APPCONST; import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator; import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.greendao.entity.Book; import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.ui.bookinfo.BookDetailedActivity;
import xyz.fycz.myreader.ui.bookinfo.BookInfoActivity; import xyz.fycz.myreader.ui.bookinfo.BookInfoActivity;
import xyz.fycz.myreader.ui.read.ReadActivity; import xyz.fycz.myreader.ui.read.ReadActivity;
import xyz.fycz.myreader.util.StringHelper; import xyz.fycz.myreader.util.StringHelper;
@ -127,7 +128,7 @@ public class BookcaseDetailedAdapter extends BookcaseAdapter {
mContext.startActivity(intent); mContext.startActivity(intent);
}); });
viewHolder.ivBookImg.setOnClickListener(v -> { viewHolder.ivBookImg.setOnClickListener(v -> {
Intent intent = new Intent(mContext, BookInfoActivity.class); Intent intent = new Intent(mContext, BookDetailedActivity.class);
intent.putExtra(APPCONST.BOOK, book); intent.putExtra(APPCONST.BOOK, book);
mContext.startActivity(intent); mContext.startActivity(intent);
}); });

@ -64,6 +64,7 @@ public class BookcaseFragment extends Fragment {
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
unbinder.unbind(); unbinder.unbind();
mBookcasePresenter.destroy();
} }
@Override @Override

@ -2,29 +2,25 @@ package xyz.fycz.myreader.ui.home.bookcase;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.DialogInterface; import android.app.Notification;
import android.content.Intent; import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.*;
import android.graphics.BitmapFactory;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.PopupMenu; import android.widget.PopupMenu;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
import com.scwang.smartrefresh.layout.listener.OnRefreshListener;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
@ -58,11 +54,11 @@ import xyz.fycz.myreader.ui.home.MainActivity;
import xyz.fycz.myreader.ui.search.SearchBookActivity; import xyz.fycz.myreader.ui.search.SearchBookActivity;
import xyz.fycz.myreader.ui.user.LoginActivity; import xyz.fycz.myreader.ui.user.LoginActivity;
import xyz.fycz.myreader.util.*; import xyz.fycz.myreader.util.*;
import xyz.fycz.myreader.util.notification.NotificationClickReceiver;
import xyz.fycz.myreader.util.notification.NotificationUtil;
import xyz.fycz.myreader.util.utils.NetworkUtils; import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.webapi.CommonApi; import xyz.fycz.myreader.webapi.CommonApi;
import static xyz.fycz.myreader.application.MyApplication.checkVersionByServer;
public class BookcasePresenter implements BasePresenter { public class BookcasePresenter implements BasePresenter {
@ -85,16 +81,26 @@ public class BookcasePresenter implements BasePresenter {
return es; return es;
} }
private String downloadingBook; private NotificationUtil notificationUtil;//通知工具类
private String downloadingChapter; private String downloadingBook;//正在下载的书名
private boolean isDownloadFinish; private String downloadingChapter;//正在下载的章节名
private boolean isStopDownload; private boolean isDownloadFinish = true;//单本书是否下载完成
private int downloadProcess; private static boolean isStopDownload = true;//是否停止下载
private PopupMenu pm; private int curCacheChapterNum;//当前下载的章节数
private int needCacheChapterNum;//需要下载的章节数
private int tempCacheChapterNum;//上次下载的章节数
private int tempCount;//下载超时时间
private int downloadInterval = 150;//下载间隔
private Runnable sendDownloadNotification;//发送通知的线程
private PopupMenu pm;//菜单
public static final String CANCEL_ACTION = "cancelAction";
private final String[] backupMenu = { private final String[] backupMenu = {
MyApplication.getmContext().getResources().getString(R.string.menu_backup_backup), MyApplication.getmContext().getResources().getString(R.string.menu_backup_backup),
MyApplication.getmContext().getResources().getString(R.string.menu_backup_restore), MyApplication.getmContext().getResources().getString(R.string.menu_backup_restore),
}; };
private final String[] webSynMenu = { private final String[] webSynMenu = {
MyApplication.getmContext().getString(R.string.menu_backup_webBackup), MyApplication.getmContext().getString(R.string.menu_backup_webBackup),
MyApplication.getmContext().getString(R.string.menu_backup_webRestore), MyApplication.getmContext().getString(R.string.menu_backup_webRestore),
@ -124,6 +130,9 @@ public class BookcasePresenter implements BasePresenter {
break; break;
case 4: case 4:
showErrorLoadingBooks(); showErrorLoadingBooks();
if (MyApplication.isApkInDebug(mMainActivity)) {
downloadAll(false);
}
break; break;
case 5: case 5:
backup(); backup();
@ -135,7 +144,7 @@ public class BookcasePresenter implements BasePresenter {
init(); init();
break; break;
case 8: case 8:
sendNotification(downloadingBook); sendNotification();
break; break;
case 9: case 9:
mBookcaseFragment.getRlDownloadTip().setVisibility(View.GONE); mBookcaseFragment.getRlDownloadTip().setVisibility(View.GONE);
@ -150,10 +159,8 @@ public class BookcasePresenter implements BasePresenter {
MyApplication.runOnUiThread(() -> createMenu()); MyApplication.runOnUiThread(() -> createMenu());
break; break;
case 12: case 12:
mBookcaseFragment.getTvStopDownload().setVisibility(View.GONE); TextHelper.showText("正在后台缓存书籍,具体进度可查看通知栏!");
break; notificationUtil.requestNotificationPermissionDialog(mMainActivity);
case 13:
mBookcaseFragment.getTvStopDownload().setVisibility(View.VISIBLE);
break; break;
} }
} }
@ -178,6 +185,10 @@ public class BookcasePresenter implements BasePresenter {
if (mSetting.isAutoSyn() && UserService.isLogin()) { if (mSetting.isAutoSyn() && UserService.isLogin()) {
synBookcaseToWeb(true); synBookcaseToWeb(true);
} }
sendDownloadNotification = this::sendNotification;
notificationUtil = NotificationUtil.getInstance();
getData(); getData();
//是否启用下拉刷新(默认启用) //是否启用下拉刷新(默认启用)
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
@ -211,16 +222,6 @@ public class BookcasePresenter implements BasePresenter {
}); });
} }
//停止按钮监听器
mBookcaseFragment.getTvStopDownload().setOnClickListener(v -> {
if (downloadProcess == 99) {
TextHelper.showText("开始缓存下一本书籍!");
isDownloadFinish = true;
} else {
isStopDownload = true;
}
});
} }
/** /**
@ -511,9 +512,16 @@ public class BookcasePresenter implements BasePresenter {
.show(); .show();
break; break;
case R.id.action_download_all: case R.id.action_download_all:
DialogCreator.createCommonDialog(mMainActivity, "一键缓存(实验)", if (!SharedPreUtils.getInstance().getBoolean("isReadDownloadAllTip")) {
DialogCreator.createCommonDialog(mMainActivity, "一键缓存",
mMainActivity.getString(R.string.all_cathe_tip), true, mMainActivity.getString(R.string.all_cathe_tip), true,
(dialog, which) -> downloadAll(), null); (dialog, which) -> {
downloadAll(true);
SharedPreUtils.getInstance().putBoolean("isReadDownloadAllTip", true);
}, null);
}else {
downloadAll(true);
}
return true; return true;
case R.id.action_backup: case R.id.action_backup:
@ -628,39 +636,64 @@ public class BookcasePresenter implements BasePresenter {
/** /**
* 缓存所有书籍 * 缓存所有书籍
*/ */
private void downloadAll() { private void downloadAll(boolean isDownloadAllChapters) {
if (!NetworkUtils.isNetWorkAvailable()) { if (!NetworkUtils.isNetWorkAvailable()) {
TextHelper.showText("无网络连接!"); TextHelper.showText("无网络连接!");
return; return;
} }
mHandler.sendMessage(mHandler.obtainMessage(13)); if (isDownloadAllChapters) {
isStopDownload = false; mHandler.sendEmptyMessage(12);
}
MyApplication.getApplication().newThread(() -> { MyApplication.getApplication().newThread(() -> {
downloadFor: ArrayList<Book> needDownloadBooks = new ArrayList<>();
for (final Book book : mBooks) { for (Book book : mBooks) {
if (BookSource.pinshu.toString().equals(book.getSource()) || "本地书籍".equals(book.getType())) { if (!BookSource.pinshu.toString().equals(book.getSource()) && !"本地书籍".equals(book.getType())) {
continue; needDownloadBooks.add(book);
} }
}
downloadFor:
for (final Book book : needDownloadBooks) {
isDownloadFinish = false; isDownloadFinish = false;
Thread downloadThread = new Thread(() -> { Thread downloadThread = new Thread(() -> {
ArrayList<Chapter> chapters = (ArrayList<Chapter>) mChapterService.findBookAllChapterByBookId(book.getId()); ArrayList<Chapter> chapters = (ArrayList<Chapter>) mChapterService.findBookAllChapterByBookId(book.getId());
int end;
if (isDownloadAllChapters) {
end = chapters.size();
} else {
end = book.getHisttoryChapterNum() + 5;
}
addDownload(book, chapters, addDownload(book, chapters,
book.getHisttoryChapterNum(), chapters.size()); book.getHisttoryChapterNum(), end, true);
}); });
es.submit(downloadThread); es.submit(downloadThread);
do { do {
try { try {
Thread.sleep(800); Thread.sleep(downloadInterval);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
if (isStopDownload) { if (isStopDownload) {
DialogCreator.createTipDialog(mMainActivity, "请等待当前书籍缓存完成后缓存任务将全部停止!");
break downloadFor; break downloadFor;
} }
} while (!isDownloadFinish); } while (!isDownloadFinish);
} }
mHandler.sendMessage(mHandler.obtainMessage(12)); if (isDownloadAllChapters && !isStopDownload) {
//通知
Intent mainIntent = new Intent(mMainActivity, MainActivity.class);
PendingIntent mainPendingIntent = PendingIntent.getActivity(mMainActivity, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = notificationUtil.build(APPCONST.channelIdDownload)
.setSmallIcon(R.drawable.ic_download)
//通知栏大图标
.setLargeIcon(BitmapFactory.decodeResource(MyApplication.getApplication().getResources(), R.mipmap.ic_launcher))
.setOngoing(false)
//点击通知后自动清除
.setAutoCancel(true)
.setContentTitle("缓存完成")
.setContentText("书籍一键缓存完成!")
.setContentIntent(mainPendingIntent)
.build();
notificationUtil.notify(1002, notification);
}
}); });
} }
@ -672,7 +705,7 @@ public class BookcasePresenter implements BasePresenter {
* @param begin * @param begin
* @param end * @param end
*/ */
public void addDownload(final Book book, final ArrayList<Chapter> mChapters, int begin, int end) { public void addDownload(final Book book, final ArrayList<Chapter> mChapters, int begin, int end, boolean isDownloadAll) {
if ("本地书籍".equals(book.getType())) { if ("本地书籍".equals(book.getType())) {
TextHelper.showText("《" + book.getName() + "》是本地书籍,不能缓存"); TextHelper.showText("《" + book.getName() + "》是本地书籍,不能缓存");
return; return;
@ -681,54 +714,71 @@ public class BookcasePresenter implements BasePresenter {
TextHelper.showText("《" + book.getName() + "》章节目录为空,缓存失败,请刷新后重试"); TextHelper.showText("《" + book.getName() + "》章节目录为空,缓存失败,请刷新后重试");
return; return;
} }
mHandler.sendMessage(mHandler.obtainMessage(10)); //取消之前下载
if (!isDownloadAll) {
if (!isStopDownload) {
isStopDownload = true;
try {
Thread.sleep(2 * downloadInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//mHandler.sendMessage(mHandler.obtainMessage(10));
downloadingBook = book.getName(); downloadingBook = book.getName();
final int finalBegin = Math.max(0, begin); final int finalBegin = Math.max(0, begin);
final int finalEnd = Math.min(end, mChapters.size()); final int finalEnd = Math.min(end, mChapters.size());
final int needCacheChapterNum = finalEnd - finalBegin; needCacheChapterNum = finalEnd - finalBegin;
final int[] curCacheChapterNum = {0}; curCacheChapterNum = 0;
//final boolean[] isDownloadFinish = new boolean[1]; tempCacheChapterNum = 0;
isStopDownload = false;
ArrayList<Chapter> needDownloadChapters = new ArrayList<>();
for (int i = finalBegin; i < finalEnd; i++) { for (int i = finalBegin; i < finalEnd; i++) {
//isDownloadFinish[0] = false;
final Chapter chapter = mChapters.get(i); final Chapter chapter = mChapters.get(i);
if (StringHelper.isEmpty(chapter.getContent())) { if (StringHelper.isEmpty(chapter.getContent())) {
needDownloadChapters.add(chapter);
}
}
needCacheChapterNum = needDownloadChapters.size();
if (!isDownloadAll && needCacheChapterNum > 0) {
mHandler.sendEmptyMessage(12);
}
mHandler.postDelayed(sendDownloadNotification, 2 * downloadInterval);
for (Chapter chapter : needDownloadChapters) {
getChapterContent(book, chapter, new ResultCallback() { getChapterContent(book, chapter, new ResultCallback() {
@Override @Override
public void onFinish(Object o, int code) { public void onFinish(Object o, int code) {
curCacheChapterNum[0]++;
downloadingChapter = chapter.getTitle(); downloadingChapter = chapter.getTitle();
downloadProcess = curCacheChapterNum[0] * 100 / needCacheChapterNum;
mChapterService.saveOrUpdateChapter(chapter, (String) o); mChapterService.saveOrUpdateChapter(chapter, (String) o);
// isDownloadFinish[0] = true; curCacheChapterNum++;
mHandler.sendMessage(mHandler.obtainMessage(8));
} }
@Override @Override
public void onError(Exception e) { public void onError(Exception e) {
// isDownloadFinish[0] = true; curCacheChapterNum++;
curCacheChapterNum[0]++;
downloadProcess = curCacheChapterNum[0] * 100 / needCacheChapterNum;
mHandler.sendMessage(mHandler.obtainMessage(8));
} }
}); });
} else { try {
//isDownloadFinish[0] = true; Thread.sleep(downloadInterval);
curCacheChapterNum[0]++; } catch (InterruptedException e) {
downloadProcess = curCacheChapterNum[0] * 100 / needCacheChapterNum; e.printStackTrace();
mHandler.sendMessage(mHandler.obtainMessage(8));
} }
if (curCacheChapterNum[0] == needCacheChapterNum) { if (curCacheChapterNum == needCacheChapterNum) {
mHandler.sendMessage(mHandler.obtainMessage(9)); if (!isDownloadAll) {
isStopDownload = true;
} }
/*while (true){ mHandler.sendMessage(mHandler.obtainMessage(9));
if (isDownloadFinish[0]){
break;
} }
}*/
if (isStopDownload) { if (isStopDownload) {
break; break;
} }
} }
if (!isDownloadAll) {
if (curCacheChapterNum == needCacheChapterNum) {
TextHelper.showText("《" + book.getName() + "》" + mMainActivity.getString(R.string.download_already_all_tips));
}
}
} }
@ -742,21 +792,55 @@ public class BookcasePresenter implements BasePresenter {
if (StringHelper.isEmpty(chapter.getBookId())) { if (StringHelper.isEmpty(chapter.getBookId())) {
chapter.setBookId(mBook.getId()); chapter.setBookId(mBook.getId());
} }
if (!StringHelper.isEmpty(chapter.getContent())) {
if (resultCallback != null) {
resultCallback.onFinish(mChapterService.getChapterCatheContent(chapter), 0);
}
} else {
ReadCrawler mReadCrawler = ReadCrawlerUtil.getReadCrawler(mBook.getSource()); ReadCrawler mReadCrawler = ReadCrawlerUtil.getReadCrawler(mBook.getSource());
CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler, resultCallback); CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler, resultCallback);
} }
/**
* 发送通知
*/
private void sendNotification() {
if (curCacheChapterNum == needCacheChapterNum) {
mHandler.sendEmptyMessage(9);
notificationUtil.cancelAll();
return;
} else {
Notification notification = notificationUtil.build(APPCONST.channelIdDownload)
.setSmallIcon(R.drawable.ic_download)
//通知栏大图标
.setLargeIcon(BitmapFactory.decodeResource(MyApplication.getApplication().getResources(), R.mipmap.ic_launcher))
.setOngoing(true)
//点击通知后自动清除
.setAutoCancel(true)
.setContentTitle("正在下载:" + downloadingBook +
"[" + curCacheChapterNum + "/" + needCacheChapterNum + "]")
.setContentText(downloadingChapter == null ? " " : downloadingChapter)
.addAction(R.drawable.ic_stop_black_24dp, "停止",
notificationUtil.getChancelPendingIntent(cancelDownloadReceiver.class))
.build();
notificationUtil.notify(1000, notification);
}
if (tempCacheChapterNum < curCacheChapterNum) {
tempCount = 1500 / downloadInterval;
tempCacheChapterNum = curCacheChapterNum;
} else if (tempCacheChapterNum == curCacheChapterNum) {
tempCount--;
if (tempCount == 0) {
isDownloadFinish = true;
notificationUtil.cancel(1000);
return;
}
}
mHandler.postDelayed(sendDownloadNotification, 2 * downloadInterval);
} }
private void sendNotification(String book) { public static class cancelDownloadReceiver extends BroadcastReceiver {
mBookcaseFragment.getPbDownload().setProgress(downloadProcess); @Override
mBookcaseFragment.getTvDownloadTip().setText("正在缓存:" + book + "[" + downloadProcess + "%]"); public void onReceive(Context context, Intent intent) {
if (downloadProcess == 100) { //todo 跳转之前要处理的逻辑
mHandler.sendMessage(mHandler.obtainMessage(9)); if (CANCEL_ACTION.equals(intent.getAction())) {
isStopDownload = true;
}
} }
} }
@ -841,6 +925,19 @@ public class BookcasePresenter implements BasePresenter {
public void cancelEdit() { public void cancelEdit() {
editBookcase(false); editBookcase(false);
} }
/**
*
*/
public void destroy() {
notificationUtil.cancelAll();
mHandler.removeCallbacks(sendDownloadNotification);
for (int i = 0; i < 13; i++) {
mHandler.removeMessages(i + 1);
}
}
/*class NotificationService extends Service{ /*class NotificationService extends Service{
@Nullable @Nullable

@ -15,6 +15,7 @@ import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST; import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.entity.bookstore.BookType; import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.greendao.entity.Book; import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.ui.bookinfo.BookDetailedActivity;
import xyz.fycz.myreader.ui.bookinfo.BookInfoActivity; import xyz.fycz.myreader.ui.bookinfo.BookInfoActivity;
import xyz.fycz.myreader.ui.home.MainActivity; import xyz.fycz.myreader.ui.home.MainActivity;
import xyz.fycz.myreader.util.TextHelper; import xyz.fycz.myreader.util.TextHelper;
@ -175,7 +176,7 @@ public class BookStorePresenter implements BasePresenter {
mBookStoreBookAdapter.setOnItemClickListener(new BookStoreBookAdapter.OnItemClickListener() { mBookStoreBookAdapter.setOnItemClickListener(new BookStoreBookAdapter.OnItemClickListener() {
@Override @Override
public void onClick(int pos, View view) { public void onClick(int pos, View view) {
Intent intent = new Intent(mBookStoreFragment.getActivity(), BookInfoActivity.class); Intent intent = new Intent(mBookStoreFragment.getActivity(), BookDetailedActivity.class);
intent.putExtra(APPCONST.BOOK, bookList.get(pos)); intent.putExtra(APPCONST.BOOK, bookList.get(pos));
mBookStoreFragment.getActivity().startActivity(intent); mBookStoreFragment.getActivity().startActivity(intent);
} }

@ -2,11 +2,13 @@ package xyz.fycz.myreader.ui.read;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Dialog; import android.app.Dialog;
import android.app.Notification;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
@ -43,11 +45,14 @@ import xyz.fycz.myreader.greendao.service.BookMarkService;
import xyz.fycz.myreader.greendao.service.BookService; import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.greendao.service.ChapterService; import xyz.fycz.myreader.greendao.service.ChapterService;
import xyz.fycz.myreader.ui.font.FontsActivity; import xyz.fycz.myreader.ui.font.FontsActivity;
import xyz.fycz.myreader.ui.home.bookcase.BookcasePresenter;
import xyz.fycz.myreader.ui.read.catalog.CatalogActivity; import xyz.fycz.myreader.ui.read.catalog.CatalogActivity;
import xyz.fycz.myreader.util.BrightUtil; import xyz.fycz.myreader.util.BrightUtil;
import xyz.fycz.myreader.util.ScreenHelper; import xyz.fycz.myreader.util.ScreenHelper;
import xyz.fycz.myreader.util.StringHelper; import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.TextHelper; import xyz.fycz.myreader.util.TextHelper;
import xyz.fycz.myreader.util.notification.NotificationClickReceiver;
import xyz.fycz.myreader.util.notification.NotificationUtil;
import xyz.fycz.myreader.util.utils.NetworkUtils; import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.webapi.CommonApi; import xyz.fycz.myreader.webapi.CommonApi;
import xyz.fycz.myreader.widget.page.LocalPageLoader; import xyz.fycz.myreader.widget.page.LocalPageLoader;
@ -68,6 +73,7 @@ public class ReadPresenter implements BasePresenter {
private ChapterService mChapterService; private ChapterService mChapterService;
private BookService mBookService; private BookService mBookService;
private BookMarkService mBookMarkService; private BookMarkService mBookMarkService;
private NotificationUtil notificationUtil;
private Setting mSetting; private Setting mSetting;
private boolean settingChange;//是否是设置改变 private boolean settingChange;//是否是设置改变
@ -95,6 +101,12 @@ public class ReadPresenter implements BasePresenter {
private Runnable keepScreenRunnable;//息屏线程 private Runnable keepScreenRunnable;//息屏线程
private Runnable autoPageRunnable;//自动翻页 private Runnable autoPageRunnable;//自动翻页
private Runnable upHpbNextPage;//更新自动翻页进度条 private Runnable upHpbNextPage;//更新自动翻页进度条
private Runnable sendDownloadNotification;
private static boolean isStopDownload = true;
private int tempCacheChapterNum;
private int tempCount;
private String downloadingChapter;
private ReadCrawler mReadCrawler; private ReadCrawler mReadCrawler;
@ -104,6 +116,8 @@ public class ReadPresenter implements BasePresenter {
private int upHpbInterval = 30;//更新翻页进度速度 private int upHpbInterval = 30;//更新翻页进度速度
private int downloadInterval = 150;
private final CharSequence[] pageMode = { private final CharSequence[] pageMode = {
"覆盖", "仿真", "滑动", "滚动", "无动画" "覆盖", "仿真", "滑动", "滚动", "无动画"
}; };
@ -156,6 +170,10 @@ public class ReadPresenter implements BasePresenter {
case 8: case 8:
mReadActivity.getPbLoading().setVisibility(View.GONE); mReadActivity.getPbLoading().setVisibility(View.GONE);
break; break;
case 9:
TextHelper.showText("正在后台缓存书籍,具体进度可查看通知栏!");
notificationUtil.requestNotificationPermissionDialog(mReadActivity);
break;
} }
} }
}; };
@ -168,20 +186,26 @@ public class ReadPresenter implements BasePresenter {
int level = intent.getIntExtra("level", 0); int level = intent.getIntExtra("level", 0);
try { try {
mPageLoader.updateBattery(level); mPageLoader.updateBattery(level);
}catch (Exception e){ } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
// 监听分钟的变化 // 监听分钟的变化
else if (Intent.ACTION_TIME_TICK.equals(intent.getAction())) { else if (Intent.ACTION_TIME_TICK.equals(intent.getAction())) {
try {
mPageLoader.updateTime(); mPageLoader.updateTime();
} catch (Exception e) {
e.printStackTrace();
}
} }
} }
}; };
public ReadPresenter(ReadActivity readActivity) { public ReadPresenter(ReadActivity readActivity) {
mReadActivity = readActivity; mReadActivity = readActivity;
mBookService = BookService.getInstance();; mBookService = BookService.getInstance();
;
mChapterService = ChapterService.getInstance(); mChapterService = ChapterService.getInstance();
mBookMarkService = BookMarkService.getInstance(); mBookMarkService = BookMarkService.getInstance();
mSetting = SysManager.getSetting(); mSetting = SysManager.getSetting();
@ -197,6 +221,9 @@ public class ReadPresenter implements BasePresenter {
upHpbNextPage = this::upHpbNextPage; upHpbNextPage = this::upHpbNextPage;
sendDownloadNotification = this::sendNotification;
notificationUtil = NotificationUtil.getInstance();
//注册广播 //注册广播
IntentFilter intentFilter = new IntentFilter(); IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
@ -393,7 +420,7 @@ public class ReadPresenter implements BasePresenter {
Intent intent = new Intent(mReadActivity, FontsActivity.class); Intent intent = new Intent(mReadActivity, FontsActivity.class);
mReadActivity.startActivityForResult(intent, APPCONST.REQUEST_FONT); mReadActivity.startActivityForResult(intent, APPCONST.REQUEST_FONT);
}, this::showPageModeDialog, v -> { }, this::showPageModeDialog, v -> {
if (mSetting.getPageMode() == PageMode.SCROLL){ if (mSetting.getPageMode() == PageMode.SCROLL) {
TextHelper.showText("滚动暂时不支持自动翻页"); TextHelper.showText("滚动暂时不支持自动翻页");
return; return;
} }
@ -509,7 +536,7 @@ public class ReadPresenter implements BasePresenter {
/** /**
* 初始化监听器 * 初始化监听器
*/ */
private void initListener(){ private void initListener() {
mReadActivity.getSrlContent().setTouchListener(new PageView.TouchListener() { mReadActivity.getSrlContent().setTouchListener(new PageView.TouchListener() {
@Override @Override
@ -523,7 +550,7 @@ public class ReadPresenter implements BasePresenter {
if (mPageLoader.getPageStatus() == PageLoader.STATUS_FINISH) { if (mPageLoader.getPageStatus() == PageLoader.STATUS_FINISH) {
showSettingView(); showSettingView();
} }
if (autoPage){ if (autoPage) {
autoPageStop(); autoPageStop();
} }
} }
@ -539,7 +566,7 @@ public class ReadPresenter implements BasePresenter {
if (!hasNextPage && endPageTipCount == 3) { if (!hasNextPage && endPageTipCount == 3) {
mReadActivity.getTvEndPageTip().setVisibility(View.VISIBLE); mReadActivity.getTvEndPageTip().setVisibility(View.VISIBLE);
mHandler.sendMessage(mHandler.obtainMessage(5)); mHandler.sendMessage(mHandler.obtainMessage(5));
if (autoPage){ if (autoPage) {
autoPageStop(); autoPageStop();
} }
} }
@ -604,8 +631,6 @@ public class ReadPresenter implements BasePresenter {
} }
/** /**
* 章节数据网络同步 * 章节数据网络同步
*/ */
@ -614,7 +639,7 @@ public class ReadPresenter implements BasePresenter {
if (!isCollected || mChapters.size() == 0 || ("本地书籍".equals(mBook.getType()) && if (!isCollected || mChapters.size() == 0 || ("本地书籍".equals(mBook.getType()) &&
!ChapterService.isChapterCached(mBook.getId(), mChapters.get(0).getTitle()))) { !ChapterService.isChapterCached(mBook.getId(), mChapters.get(0).getTitle()))) {
if ("本地书籍".equals(mBook.getType())) { if ("本地书籍".equals(mBook.getType())) {
if (!new File(mBook.getChapterUrl()).exists()){ if (!new File(mBook.getChapterUrl()).exists()) {
TextHelper.showText("书籍缓存为空且源文件不存在,书籍加载失败!"); TextHelper.showText("书籍缓存为空且源文件不存在,书籍加载失败!");
mReadActivity.finish(); mReadActivity.finish();
return; return;
@ -729,19 +754,10 @@ public class ReadPresenter implements BasePresenter {
TextHelper.showText("无网络连接!"); TextHelper.showText("无网络连接!");
return; return;
} }
MyApplication.runOnUiThread(() ->{
new AlertDialog.Builder(mReadActivity) new AlertDialog.Builder(mReadActivity)
.setTitle("缓存书籍") .setTitle("缓存书籍")
.setSingleChoiceItems(APPCONST.DIALOG_DOWNLOAD, selectedIndex, new DialogInterface.OnClickListener() { .setSingleChoiceItems(APPCONST.DIALOG_DOWNLOAD, selectedIndex, (dialog, which) -> selectedIndex = which).setNegativeButton("取消", ((dialog, which) -> dialog.dismiss())).setPositiveButton("确定",
@Override
public void onClick(DialogInterface dialog, int which) {
selectedIndex = which;
}
}).setNegativeButton("取消", (new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})).setPositiveButton("确定",
(dialog, which) -> { (dialog, which) -> {
switch (selectedIndex) { switch (selectedIndex) {
case 0: case 0:
@ -758,22 +774,43 @@ public class ReadPresenter implements BasePresenter {
break; break;
} }
}).show(); }).show();
});
} }
private void addDownload(final TextView tvDownloadProgress, int begin, int end) { private void addDownload(final TextView tvDownloadProgress, int begin, int end) {
/*//取消之前下载
if (!isStopDownload) {
isStopDownload = true;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}*/
//计算断点章节 //计算断点章节
final int finalBegin = Math.max(0, begin); final int finalBegin = Math.max(0, begin);
final int finalEnd = Math.min(end, mChapters.size()); final int finalEnd = Math.min(end, mChapters.size());
needCacheChapterNum = finalEnd - finalBegin; needCacheChapterNum = finalEnd - finalBegin;
curCacheChapterNum = 0; curCacheChapterNum = 0;
MyApplication.getApplication().newThread(() -> { isStopDownload = false;
ArrayList<Chapter> needDownloadChapters = new ArrayList<>();
for (int i = finalBegin; i < finalEnd; i++) { for (int i = finalBegin; i < finalEnd; i++) {
final Chapter chapter = mChapters.get(i); final Chapter chapter = mChapters.get(i);
if (StringHelper.isEmpty(chapter.getContent())) { if (StringHelper.isEmpty(chapter.getContent())) {
needDownloadChapters.add(chapter);
}
}
needCacheChapterNum = needDownloadChapters.size();
if (needCacheChapterNum > 0) {
mHandler.sendEmptyMessage(9);
mHandler.postDelayed(sendDownloadNotification, 2 * downloadInterval);
}
MyApplication.getApplication().newThread(() -> {
for (Chapter chapter : needDownloadChapters) {
getChapterContent(chapter, new ResultCallback() { getChapterContent(chapter, new ResultCallback() {
@Override @Override
public void onFinish(Object o, int code) { public void onFinish(Object o, int code) {
// chapter.setContent((String) o); downloadingChapter = chapter.getTitle();
mChapterService.saveOrUpdateChapter(chapter, (String) o); mChapterService.saveOrUpdateChapter(chapter, (String) o);
curCacheChapterNum++; curCacheChapterNum++;
mHandler.sendMessage(mHandler.obtainMessage(3, tvDownloadProgress)); mHandler.sendMessage(mHandler.obtainMessage(3, tvDownloadProgress));
@ -785,9 +822,13 @@ public class ReadPresenter implements BasePresenter {
mHandler.sendMessage(mHandler.obtainMessage(3, tvDownloadProgress)); mHandler.sendMessage(mHandler.obtainMessage(3, tvDownloadProgress));
} }
}); });
} else { try {
curCacheChapterNum++; Thread.sleep(downloadInterval);
mHandler.sendMessage(mHandler.obtainMessage(3, tvDownloadProgress)); } catch (InterruptedException e) {
e.printStackTrace();
}
if (isStopDownload) {
break;
} }
} }
if (curCacheChapterNum == needCacheChapterNum) { if (curCacheChapterNum == needCacheChapterNum) {
@ -805,6 +846,52 @@ public class ReadPresenter implements BasePresenter {
} }
} }
/**
* 发送通知
*/
private void sendNotification() {
if (curCacheChapterNum == needCacheChapterNum) {
notificationUtil.cancel(1001);
return;
} else {
Notification notification = notificationUtil.build(APPCONST.channelIdDownload)
.setSmallIcon(R.drawable.ic_download)
//通知栏大图标
.setLargeIcon(BitmapFactory.decodeResource(MyApplication.getApplication().getResources(), R.mipmap.ic_launcher))
.setOngoing(true)
//点击通知后自动清除
.setAutoCancel(true)
.setContentTitle("正在下载:" + mBook.getName() +
"[" + curCacheChapterNum + "/" + needCacheChapterNum + "]")
.setContentText(downloadingChapter == null ? " " : downloadingChapter)
.addAction(R.drawable.ic_stop_black_24dp, "停止",
notificationUtil.getChancelPendingIntent(cancelDownloadReceiver.class))
.build();
notificationUtil.notify(1001, notification);
}
if (tempCacheChapterNum < curCacheChapterNum) {
tempCount = 1500 / downloadInterval;
tempCacheChapterNum = curCacheChapterNum;
} else if (tempCacheChapterNum == curCacheChapterNum) {
tempCount--;
if (tempCount == 0) {
notificationUtil.cancel(1001);
return;
}
}
mHandler.postDelayed(sendDownloadNotification, 2 * downloadInterval);
}
public static class cancelDownloadReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//todo 跳转之前要处理的逻辑
if (NotificationClickReceiver.CANCEL_ACTION.equals(intent.getAction())) {
isStopDownload = true;
}
}
}
/** /**
* 获取章节内容 * 获取章节内容
* *
@ -982,6 +1069,7 @@ public class ReadPresenter implements BasePresenter {
/** /**
* 添加本地书籍 * 添加本地书籍
*
* @param path * @param path
*/ */
private void addLocalBook(String path) { private void addLocalBook(String path) {
@ -1001,7 +1089,7 @@ public class ReadPresenter implements BasePresenter {
//判断书籍是否已经添加 //判断书籍是否已经添加
Book existsBook = mBookService.findBookByAuthorAndName(book.getName(), book.getAuthor()); Book existsBook = mBookService.findBookByAuthorAndName(book.getName(), book.getAuthor());
if (book.equals(existsBook)){ if (book.equals(existsBook)) {
mBook = existsBook; mBook = existsBook;
return; return;
} }
@ -1012,10 +1100,11 @@ public class ReadPresenter implements BasePresenter {
/** /**
* 跳转到指定章节的指定页面 * 跳转到指定章节的指定页面
*
* @param chapterPos * @param chapterPos
* @param pagePos * @param pagePos
*/ */
private void skipToChapterAndPage(final int chapterPos, final int pagePos){ private void skipToChapterAndPage(final int chapterPos, final int pagePos) {
isPrev = false; isPrev = false;
if (StringHelper.isEmpty(mChapters.get(chapterPos).getContent())) { if (StringHelper.isEmpty(mChapters.get(chapterPos).getContent())) {
if ("本地书籍".equals(mBook.getType())) { if ("本地书籍".equals(mBook.getType())) {
@ -1122,10 +1211,16 @@ public class ReadPresenter implements BasePresenter {
*/ */
public void onDestroy() { public void onDestroy() {
mReadActivity.unregisterReceiver(mReceiver); mReadActivity.unregisterReceiver(mReceiver);
mHandler.removeCallbacks(keepScreenRunnable);
mHandler.removeCallbacks(upHpbNextPage);
mHandler.removeCallbacks(autoPageRunnable);
/*mHandler.removeCallbacks(sendDownloadNotification);
notificationUtil.cancelAll();
MyApplication.getApplication().shutdownThreadPool();*/
if (autoPage) { if (autoPage) {
autoPageStop(); autoPageStop();
} }
for (int i = 0; i < 8; i++) { for (int i = 0; i < 9; i++) {
mHandler.removeMessages(i + 1); mHandler.removeMessages(i + 1);
} }
mPageLoader.closeBook(); mPageLoader.closeBook();

@ -22,6 +22,7 @@ import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.crawler.*; import xyz.fycz.myreader.crawler.*;
import xyz.fycz.myreader.entity.SearchBookBean; import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap; import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.ui.bookinfo.BookDetailedActivity;
import xyz.fycz.myreader.ui.bookinfo.BookInfoActivity; import xyz.fycz.myreader.ui.bookinfo.BookInfoActivity;
import xyz.fycz.myreader.R; import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.BasePresenter; import xyz.fycz.myreader.base.BasePresenter;
@ -141,7 +142,7 @@ public class SearchBookPrensenter implements BasePresenter {
}); });
//进入书籍详情页 //进入书籍详情页
mSearchBookActivity.getGvSearchBooksList().setOnItemClickListener((adapterView, view, i, l) -> { mSearchBookActivity.getGvSearchBooksList().setOnItemClickListener((adapterView, view, i, l) -> {
Intent intent = new Intent(mSearchBookActivity, BookInfoActivity.class); Intent intent = new Intent(mSearchBookActivity, BookDetailedActivity.class);
intent.putExtra(APPCONST.SEARCH_BOOK_BEAN, new ArrayList<>(mBooks.getValues(mBooksBean.get(i)))); intent.putExtra(APPCONST.SEARCH_BOOK_BEAN, new ArrayList<>(mBooks.getValues(mBooksBean.get(i))));
mSearchBookActivity.startActivity(intent); mSearchBookActivity.startActivity(intent);
}); });

@ -219,9 +219,7 @@ public class HttpUtil {
} }
public static void sendGetRequest_okHttp(final String address, final HttpCallback callback) { public static void sendGetRequest_okHttp(final String address, final HttpCallback callback) {
MyApplication.getApplication().newThread(new Runnable() { MyApplication.getApplication().newThread(() -> {
@Override
public void run() {
/* HttpURLConnection connection = null; /* HttpURLConnection connection = null;
try { try {
URL url = new URL(address); URL url = new URL(address);
@ -265,8 +263,6 @@ public class HttpUtil {
e.printStackTrace(); e.printStackTrace();
callback.onError(e); callback.onError(e);
} }
}
}); });
} }

@ -11,21 +11,11 @@ public class TextHelper {
public static void showText(final String text){ public static void showText(final String text){
MyApplication.runOnUiThread(new Runnable() { MyApplication.runOnUiThread(() -> Toast.makeText(MyApplication.getApplication(),text, Toast.LENGTH_SHORT).show());
@Override
public void run() {
Toast.makeText(MyApplication.getApplication(),text, Toast.LENGTH_SHORT).show();
}
});
} }
public static void showLongText(final String text){ public static void showLongText(final String text){
MyApplication.runOnUiThread(new Runnable() { MyApplication.runOnUiThread(() -> Toast.makeText(MyApplication.getApplication(),text, Toast.LENGTH_LONG).show());
@Override
public void run() {
Toast.makeText(MyApplication.getApplication(),text, Toast.LENGTH_LONG).show();
}
});
} }
} }

@ -0,0 +1,23 @@
package xyz.fycz.myreader.util.notification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import xyz.fycz.myreader.application.MyApplication;
/**
* @author fengyue
* @date 2020/8/14 22:04
*/
public class NotificationClickReceiver extends BroadcastReceiver {
public static final String CANCEL_ACTION = "cancelAction";
@Override
public void onReceive(Context context, Intent intent) {
//todo 跳转之前要处理的逻辑
if (CANCEL_ACTION.equals(intent.getAction())){
MyApplication.getApplication().shutdownThreadPool();
}
}
}

@ -0,0 +1,171 @@
package xyz.fycz.myreader.util.notification;
import android.annotation.TargetApi;
import android.app.*;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.greendao.service.BookMarkService;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import static xyz.fycz.myreader.util.notification.NotificationClickReceiver.CANCEL_ACTION;
/**
* @author fengyue
* @date 2020/8/14 22:07
*/
public class NotificationUtil {
private static volatile NotificationUtil sInstance;
private NotificationManager notificationManager;
public static NotificationUtil getInstance() {
if (sInstance == null){
synchronized (NotificationUtil.class){
if (sInstance == null){
sInstance = new NotificationUtil();
}
}
}
return sInstance;
}
public NotificationUtil() {
notificationManager = (NotificationManager) MyApplication.getmContext().getSystemService(Context.NOTIFICATION_SERVICE);
}
public NotificationCompat.Builder createBuilder(Context context, String channelId){
return new NotificationCompat.Builder(context, channelId);
}
@TargetApi(26)
public void createNotificationChannel(String channelId, String channelName) {
NotificationManager notificationManager = (NotificationManager) MyApplication.getApplication().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW);
channel.enableLights(true);//是否在桌面icon右上角展示小红点
channel.setLightColor(Color.RED);//小红点颜色
channel.setShowBadge(false); //是否在久按桌面图标时显示此渠道的通知
notificationManager.createNotificationChannel(channel);
}
public NotificationCompat.Builder build(String channelId){
return new NotificationCompat.Builder(MyApplication.getmContext(), channelId);
}
public void sendDownloadNotification(String title, String text, PendingIntent pendingIntent){
NotificationCompat.Builder builder = build(APPCONST.channelIdDownload)
.setSmallIcon(R.drawable.ic_download)
//通知栏大图标
.setLargeIcon(BitmapFactory.decodeResource(MyApplication.getApplication().getResources(), R.mipmap.ic_launcher))
.setOngoing(true)
//点击通知后自动清除
.setAutoCancel(true)
.setContentTitle(title)
.setContentText(text);
if (pendingIntent == null) {
pendingIntent = getChancelPendingIntent(NotificationClickReceiver.class);
}
builder.addAction(R.drawable.ic_stop_black_24dp, "停止", pendingIntent);
notificationManager.notify(1000, builder.build());
}
public PendingIntent getChancelPendingIntent(Class<?> clz) {
Intent intent = new Intent(MyApplication.getmContext(), clz);
intent.setAction(CANCEL_ACTION);
return PendingIntent.getBroadcast(MyApplication.getmContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public void cancel(int id){
notificationManager.cancel(id);
}
public void cancelAll(){
notificationManager.cancelAll();
}
public void notify(int id, Notification notification){
notificationManager.notify(id, notification);
}
/**
* 跳到通知栏设置界面
* @param context
*/
public void requestNotificationPermission(Context context){
if (!isNotificationEnabled(context)) {
try {
// 根据isOpened结果,判断是否需要提醒用户跳转AppInfo页面,去打开App通知权限
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
//这种方案适用于 API 26, 即8.0(含8.0)以上可以用
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
intent.putExtra(Settings.EXTRA_CHANNEL_ID, context.getApplicationInfo().uid);
//这种方案适用于 API21——25,即 5.0——7.1 之间的版本可以使用
intent.putExtra("app_package", context.getPackageName());
intent.putExtra("app_uid", context.getApplicationInfo().uid);
// 小米6 -MIUI9.6-8.0.0系统,是个特例,通知设置界面只能控制"允许使用通知圆点"——然而这个玩意并没有卵用,我想对雷布斯说:I'm not ok!!!
if ("MI 6".equals(Build.MODEL)) {
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", context.getPackageName(), null);
intent.setData(uri);
intent.setAction("com.android.settings/.SubSettings");
}
context.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
// 出现异常则跳转到应用设置界面:锤子坚果3——OC105 API25
Intent intent = new Intent();
//下面这种方案是直接跳转到当前应用的设置界面。
//https://blog.csdn.net/ysy950803/article/details/71910806
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri uri = Uri.fromParts("package", context.getPackageName(), null);
intent.setData(uri);
context.startActivity(intent);
}
}
}
public void requestNotificationPermissionDialog(Context context){
if (!isNotificationEnabled(context)) {
new AlertDialog.Builder(context)
.setTitle("开启通知")
.setMessage("检测到未开启通知权限,无法在通知栏查看缓存进度,是否前往开启?")
.setCancelable(true)
.setPositiveButton("确定", (dialog, which) -> requestNotificationPermission(context))
.setNegativeButton("取消", null)
.show();
}
}
/**
* 获取通知权限
* @param context
*/
public boolean isNotificationEnabled(Context context) {
boolean isOpened = false;
try {
NotificationManagerCompat manager = NotificationManagerCompat.from(context);
isOpened = manager.areNotificationsEnabled();
} catch (Exception e) {
e.printStackTrace();
}
return isOpened;
}
}

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1065.0"
android:viewportHeight="1024.0">
<path
android:pathData="M486.4,1024h-102.4a76.9,76.9 0,0 1,-76.8 -76.8V76.8A76.9,76.9 0,0 1,384 0h102.4a76.9,76.9 0,0 1,76.8 76.8v870.4a76.9,76.9 0,0 1,-76.8 76.8zM384,51.2A25.6,25.6 0,0 0,358.4 76.8v870.4a25.6,25.6 0,0 0,25.6 25.6h102.4a25.6,25.6 0,0 0,25.6 -25.6V76.8a25.6,25.6 0,0 0,-25.6 -25.6zM179.2,1024H76.8A76.9,76.9 0,0 1,0 947.2V128a76.9,76.9 0,0 1,76.8 -76.8H179.2a76.9,76.9 0,0 1,76.8 76.8v819.2A76.9,76.9 0,0 1,179.2 1024zM76.8,102.4a25.6,25.6 0,0 0,-25.6 25.6v819.2a25.6,25.6 0,0 0,25.6 25.6H179.2a25.6,25.6 0,0 0,25.6 -25.6V128A25.6,25.6 0,0 0,179.2 102.4z"
android:fillColor="@color/black"/>
<path
android:pathData="M799.4,636.8l-130.9,-488.6a25.6,25.6 0,0 1,18.1 -31.4l98.9,-26.5a25.3,25.3 0,0 1,6.7 -0.9,25.6 25.6,0 0,1 24.7,19l141.6,528.4h53L866.3,95.2a76.7,76.7 0,0 0,-94.1 -54.3l-98.9,26.5a76.9,76.9 0,0 0,-54.3 94.1l127.4,475.3zM1065.6,841.6h-128v-128h-51.2v128h-128v51.2h128v128h51.2v-128h128v-51.2z"
android:fillColor="@color/black"/>
</vector>

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024.0"
android:viewportHeight="1024.0">
<path
android:pathData="M911.8,44L663.3,44c-63.7,0 -119.6,33.4 -150.9,83.4 -31.3,-50.1 -87.1,-83.4 -150.8,-83.4L111.3,44c-49,0 -88.7,39.5 -88.7,88.2v706.8c0,48.7 39.7,88.2 88.7,88.2h298.3c22.4,32 59.7,52.9 101.9,52.9 42.2,0 79.5,-20.9 101.9,-52.9h298.3c49,0 88.7,-39.5 88.7,-88.2L1000.6,132.2c0,-48.7 -39.7,-88.2 -88.7,-88.2zM947.3,839c0,9.3 -3.7,18.3 -10.4,24.9a35.8,35.8 0,0 1,-25.1 10.3L580.3,874.3c-7.9,30.4 -35.7,52.9 -68.8,52.9 -33.1,0 -60.9,-22.5 -68.8,-52.9L111.3,874.3c-9.4,0 -18.5,-3.7 -25.1,-10.3a35.2,35.2 0,0 1,-10.4 -24.9L75.8,132.2a35.2,35.2 0,0 1,10.4 -24.9c6.6,-6.6 15.7,-10.3 25.1,-10.3h250.4c68.6,0 124.2,55.3 124.2,123.4v473.5h0.1c1.1,13.8 12.6,24.4 26.5,24.4 13.9,0 25.5,-10.6 26.5,-24.4h0.1L539.1,220.4c0,-68.2 55.6,-123.4 124.2,-123.4h248.5a35.7,35.7 0,0 1,25.1 10.3,35.2 35.2,0 0,1 10.4,24.9v706.8h0z"
android:fillColor="@color/white"/>
</vector>

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

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleGravity="top"
app:expandedTitleMarginStart="114dp"
app:expandedTitleMarginTop="66dp"
app:expandedTitleTextAppearance="@style/Base.TextAppearance.AppCompat.Title"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<include layout="@layout/layout_book_detail_header"/>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/Theme.ToolBar.Menu"/>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<!--详情简介-->
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="56dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<include layout="@layout/layout_book_detail_content"/>
</androidx.core.widget.NestedScrollView>
<!--底部button-->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical">
<include layout="@layout/layout_book_detail_bottom"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="top"
android:background="#140000"/>
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<ProgressBar
android:id="@+id/pb_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"/>
</RelativeLayout>

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/BottomNavigation.GroupView"
android:background="@color/white">
<FrameLayout
android:id="@+id/fl_add_bookcase"
style="@style/BottomNavigation.ItemView">
<androidx.appcompat.widget.AppCompatImageView
style="@style/BottomNavigation.ItemView.Icon"
android:background="@drawable/ic_vector_add_bookcase"/>
<TextView
android:id="@+id/book_detail_tv_add"
style="@style/BottomNavigation.ItemView.Title"
android:text="加入书架"
android:textColor="@color/black"/>
</FrameLayout>
<FrameLayout
android:id="@+id/fl_open_book"
style="@style/BottomNavigation.ItemView"
android:background="?colorPrimary">
<androidx.appcompat.widget.AppCompatImageView
style="@style/BottomNavigation.ItemView.Icon"
android:background="@drawable/ic_vector_book_read"/>
<TextView
android:id="@+id/book_detail_tv_open"
style="@style/BottomNavigation.ItemView.Title"
android:text="开始阅读"
android:textColor="@color/white"/>
</FrameLayout>
</LinearLayout>

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!--简介-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="简介:"
android:textColor="@color/black"
android:textSize="18sp"/>
<TextView
android:id="@+id/book_detail_tv_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-10dp"
android:ellipsize="end"
android:maxLines="5"
android:padding="15dp"
android:lineSpacingMultiplier="1.2"
android:text="简介: "
android:textColor="@color/title_black"
android:textSize="16sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:orientation="vertical">
<TextView
android:layout_marginTop="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lineSpacingMultiplier="1.2"
android:textSize="14sp"
android:text="@string/statement"
android:textColor="@color/title_black"
/>
<TextView
android:id="@+id/tv_disclaimer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/disclaimer"
android:textColor="#2196F3"
android:textSize="14sp"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="10dp"
android:background="@color/sys_window_back"
android:visibility="gone"/>
<!--书籍目录-->
<RelativeLayout
android:id="@+id/book_detail_rl_catalog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<TextView
android:id="@+id/book_detail_tv_catalog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="15dp"
android:text="最新章节"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/book_detail_tv_catalog_more"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right"
android:padding="15dp"
android:text="更多"
android:textColor="@color/title_black"
android:textSize="15sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/book_detail_rv_catalog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/book_detail_tv_catalog"
android:foregroundGravity="center" />
</RelativeLayout>
<!--底部空白,给底部bottom预留位置-->
<!--<View-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="56dp"-->
<!--android:background="@color/divider_wide" />-->
</LinearLayout>

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorPrimary"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="64.0dp"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="16.0dp"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7">
<androidx.cardview.widget.CardView
android:id="@+id/cover"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardCornerRadius="0dp"
app:cardElevation="2dp">
<ImageView
android:id="@+id/book_detail_iv_cover"
android:layout_width="82dp"
android:layout_height="110dp"
android:scaleType="centerCrop"
android:background="@color/colorPrimary"
tools:src="@mipmap/no_image" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:id="@+id/book_detail_author_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/cover"
android:layout_marginLeft="14dp"
android:layout_marginTop="30dp"
android:layout_toRightOf="@+id/cover">
<TextView
android:id="@+id/book_detail_tv_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/white"
tools:text=" 茶叶蛋" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" | "
android:textColor="@color/little_black_white"/>
<TextView
android:id="@+id/book_detail_tv_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/white"
tools:text="仙侠" />
</LinearLayout>
<TextView
android:id="@+id/book_detail_newest_chapter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/book_detail_author_type"
android:layout_alignLeft="@id/book_detail_author_type"
android:layout_marginTop="6dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/white"
tools:text="最新章节" />
<TextView
android:id="@+id/book_detail_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/book_detail_newest_chapter"
android:layout_alignLeft="@id/book_detail_newest_chapter"
android:layout_marginTop="6dp"
android:textColor="@color/white"
tools:text="书源" />
<!--<TextView-->
<!--android:id="@+id/book_detail_tv_lately_update"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_below="@id/book_detail_tv_author"-->
<!--android:layout_marginTop="10dp"-->
<!--android:layout_toRightOf="@id/cover"-->
<!--android:ellipsize="end"-->
<!--android:singleLine="true"-->
<!--android:textColor="@color/textAssist"-->
<!--android:textSize="13sp"-->
<!--tools:text="4月前"/>-->
</RelativeLayout>

@ -0,0 +1,19 @@
<?xml version ="1.0" encoding ="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_change_source"
android:icon="@mipmap/ic_menu_exchange"
android:title="更换书源"
app:showAsAction="always" />
<item
android:id="@+id/action_reload"
android:icon="@mipmap/ic_menu_refresh"
android:title="重新加载"/>
<item
android:id="@+id/action_open_link"
android:title="打开链接" />
</menu>

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 920 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

@ -38,7 +38,7 @@
<string name="disclaimer">免责声明</string> <string name="disclaimer">免责声明</string>
<string name="statement">声明:小说数据全部从网络获取,软件不提供或保存任何小说,详情请参考免责声明:</string> <string name="statement">声明:小说数据全部从网络获取,软件不提供或保存任何小说,详情请参考免责声明:</string>
<string name="all_cathe_tip">"说明:本功能属于实验功能,书籍缓存的章节是从历史阅读章节到最新章节!\n\n存在的问题:\n1、书源为品书网的书籍因网站原因无法一键缓存!\n\n2、部分书籍缓存时会卡在99%的位置\n解决方案:点击停止按钮直接进入下一本书籍缓存/重新点击一键缓存!\n\n3、书籍缓存时进入阅读时章节加载可能变慢!\n\n确定要一键缓存?"</string> <string name="all_cathe_tip">"说明:1、书籍缓存的章节是从历史阅读章节到最新章节!\n\n2、书源为品书网的书籍因网站原因无法一键缓存!\n\n确定要一键缓存?"</string>
<!--menu--> <!--menu-->
<string name="menu_book_Top">移至顶部</string> <string name="menu_book_Top">移至顶部</string>
@ -50,7 +50,7 @@
<string name="menu_bookcase_style">切换布局</string> <string name="menu_bookcase_style">切换布局</string>
<string name="menu_bookcase_add">添加本地</string> <string name="menu_bookcase_add">添加本地</string>
<string name="menu_bookcase_syn">同步书架</string> <string name="menu_bookcase_syn">同步书架</string>
<string name="menu_bookcase_download_all">一键缓存(实验)</string> <string name="menu_bookcase_download_all">一键缓存</string>
<string name="menu_bookcase_backup">备份/恢复</string> <string name="menu_bookcase_backup">备份/恢复</string>
<string name="menu_bookcase_about">关于软件</string> <string name="menu_bookcase_about">关于软件</string>

@ -91,7 +91,44 @@
<item name="android:divider">@color/divider_common</item> <item name="android:divider">@color/divider_common</item>
<item name="android:dividerHeight">0.5dp</item> <item name="android:dividerHeight">0.5dp</item>
</style> </style>
<!--BookDetail-->
<style name="BottomNavigationViewTheme" />
<style name="BottomNavigation" />
<style name="BottomNavigation.GroupView">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">56dp</item>
<item name="android:layout_gravity">bottom</item>
<item name="android:elevation">4dp</item>
<item name="android:orientation">horizontal</item>
</style>
<style name="BottomNavigation.ItemView">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:layout_weight">1</item>
<item name="android:clickable">true</item>
</style>
<style name="BottomNavigation.ItemView.Icon" parent="">
<item name="android:layout_marginTop">8dp</item>
<item name="android:layout_width">24dp</item>
<item name="android:layout_height">24dp</item>
<item name="android:layout_gravity">top|center_horizontal</item>
<item name="android:layout_centerHorizontal">true</item>
<item name="android:layout_alignParentTop">true</item>
</style>
<style name="BottomNavigation.ItemView.Title" parent="">
<item name="android:layout_marginBottom">8dp</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_gravity">bottom|center_horizontal</item>
<item name="android:layout_centerHorizontal">true</item>
<item name="android:layout_alignParentBottom">true</item>
<item name="android:textSize">12sp</item>
</style>
</resources> </resources>

@ -1,2 +1,2 @@
#Wed Aug 12 21:27:50 CST 2020 #Sat Aug 15 23:51:09 CST 2020
VERSION_CODE=141 VERSION_CODE=142

Loading…
Cancel
Save