From f2a98582f1d982eb3a67f605de7843ad5684bce1 Mon Sep 17 00:00:00 2001 From: fengyuecanzhu <1021300691@qq.com> Date: Mon, 17 Aug 2020 13:10:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B9=A6=E7=B1=8D=E8=AF=A6?= =?UTF-8?q?=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/caches/build_file_checksums.ser | Bin 537 -> 537 bytes app/build.gradle | 3 +- app/src/main/AndroidManifest.xml | 10 +- app/src/main/assets/updatelog.fy | 6 + .../myreader/application/MyApplication.java | 33 +- .../myreader/backup/BackupAndRestore.java | 15 +- .../xyz/fycz/myreader/base/BaseActivity.java | 1 - .../base/{ => adapter}/BaseListAdapter.java | 4 +- .../xyz/fycz/myreader/common/APPCONST.java | 2 + .../ui/bookinfo/BookDetailedActivity.java | 353 ++++++++++++++++++ .../ui/filesys/FileSystemActivity.java | 4 + .../ui/filesys/FileSystemAdapter.java | 2 +- .../ui/filesys/LocalBookFragment.java | 8 +- .../ui/home/bookcase/BookcaseAdapter.java | 9 +- .../bookcase/BookcaseDetailedAdapter.java | 3 +- .../ui/home/bookcase/BookcaseFragment.java | 1 + .../ui/home/bookcase/BookcasePresenter.java | 283 +++++++++----- .../ui/home/bookstore/BookStorePresenter.java | 3 +- .../fycz/myreader/ui/read/ReadPresenter.java | 219 ++++++++--- .../ui/search/SearchBookPrensenter.java | 3 +- .../java/xyz/fycz/myreader/util/HttpUtil.java | 94 +++-- .../xyz/fycz/myreader/util/TextHelper.java | 14 +- .../NotificationClickReceiver.java | 23 ++ .../util/notification/NotificationUtil.java | 171 +++++++++ .../res/drawable/ic_vector_add_bookcase.xml | 12 + .../main/res/drawable/ic_vector_book_read.xml | 9 + app/src/main/res/drawable/selector_pwd.xml | 6 - .../main/res/layout/activity_book_detail.xml | 79 ++++ .../res/layout/layout_book_detail_bottom.xml | 43 +++ .../res/layout/layout_book_detail_content.xml | 98 +++++ .../res/layout/layout_book_detail_header.xml | 98 +++++ app/src/main/res/menu/menu_book_detail.xml | 19 + .../res/mipmap-xhdpi/ic_menu_exchange.png | Bin 0 -> 659 bytes .../main/res/mipmap-xhdpi/ic_menu_refresh.png | Bin 0 -> 901 bytes app/src/main/res/mipmap-xhdpi/pwd_gone.png | Bin 920 -> 0 bytes .../main/res/mipmap-xhdpi/pwd_visiable.png | Bin 1428 -> 0 bytes app/src/main/res/values/strings.xml | 4 +- app/src/main/res/values/styles.xml | 37 ++ app/version_code.properties | 4 +- 39 files changed, 1403 insertions(+), 270 deletions(-) rename app/src/main/java/xyz/fycz/myreader/base/{ => adapter}/BaseListAdapter.java (97%) create mode 100644 app/src/main/java/xyz/fycz/myreader/ui/bookinfo/BookDetailedActivity.java create mode 100644 app/src/main/java/xyz/fycz/myreader/util/notification/NotificationClickReceiver.java create mode 100644 app/src/main/java/xyz/fycz/myreader/util/notification/NotificationUtil.java create mode 100644 app/src/main/res/drawable/ic_vector_add_bookcase.xml create mode 100644 app/src/main/res/drawable/ic_vector_book_read.xml delete mode 100644 app/src/main/res/drawable/selector_pwd.xml create mode 100644 app/src/main/res/layout/activity_book_detail.xml create mode 100644 app/src/main/res/layout/layout_book_detail_bottom.xml create mode 100644 app/src/main/res/layout/layout_book_detail_content.xml create mode 100644 app/src/main/res/layout/layout_book_detail_header.xml create mode 100644 app/src/main/res/menu/menu_book_detail.xml create mode 100644 app/src/main/res/mipmap-xhdpi/ic_menu_exchange.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_menu_refresh.png delete mode 100644 app/src/main/res/mipmap-xhdpi/pwd_gone.png delete mode 100644 app/src/main/res/mipmap-xhdpi/pwd_visiable.png diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index a36442754d70ee78a67fcc8bc01481115d267824..bf02cc24fa3ccefcac5930908068af7d36f71e5c 100644 GIT binary patch delta 35 rcmbQqGLvP(43=Lt_xLByDHo91n6Tge44-VfOh9h-BCe*%%8cIt2AB>+ delta 35 rcmbQqGLvP(43;jQ5aEe)$^|?v%$Ag?ZTT(Q7geP(wOV7cGUGP@ @@ -74,11 +75,13 @@ android:resource="@xml/file_paths"/> - + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/updatelog.fy b/app/src/main/assets/updatelog.fy index 3f3b008..74cc7ad 100644 --- a/app/src/main/assets/updatelog.fy +++ b/app/src/main/assets/updatelog.fy @@ -1,3 +1,9 @@ +2020.08.15 +风月读书v1.20.081523 +1、修复安卓10(Q)无法访问外部储存空间及其导致的一系列问题(如:无法下载字体、无法使用备份\恢复、无法添加本地书籍、无法同步书架等) +2、修复一键缓存的bug +3、新增缓存时在通知栏提示缓存进度,并可在通知栏停止缓存 + 2020.08.12 风月读书v1.20.081221 1、新增本地书籍导入页面(不再使用系统文件管理器导入),支持一次性导入多本书籍 diff --git a/app/src/main/java/xyz/fycz/myreader/application/MyApplication.java b/app/src/main/java/xyz/fycz/myreader/application/MyApplication.java index 480a43a..ef895b0 100644 --- a/app/src/main/java/xyz/fycz/myreader/application/MyApplication.java +++ b/app/src/main/java/xyz/fycz/myreader/application/MyApplication.java @@ -13,7 +13,9 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.graphics.Color; import android.net.Uri; +import android.os.Build; import android.os.Environment; import android.os.Handler; @@ -61,6 +63,9 @@ public class MyApplication extends Application { application = this; HttpUtil.trustAllHosts();//信任所有证书 // handleSSLHandshake(); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel(); + } mFixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());//初始化线程池 BaseActivity.setCloseAntiHijacking(true); @@ -120,10 +125,12 @@ public class MyApplication extends Application { @TargetApi(26) private void createNotificationChannel() { - NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - - notificationManager.createNotificationChannel(new NotificationChannel("gxdw_push_and_im", "gxdw", NotificationManager.IMPORTANCE_DEFAULT)); + NotificationChannel channel = new NotificationChannel(APPCONST.channelIdDownload, "下载通知", NotificationManager.IMPORTANCE_LOW); + 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; try { String url = "https://shimo.im/docs/cqkgjPRRydYYhQKt/read"; - if (isApkInDebug(getmContext())){ + if (isApkInDebug(getmContext())) { url = "https://shimo.im/docs/zfzpda7MUGskOC9v/read"; } doc = Jsoup.connect(url).get(); String content = doc.getElementsByClass("ql-editor").text(); - if (StringHelper.isEmpty(content)){ + if (StringHelper.isEmpty(content)) { TextHelper.showText("检查更新失败!"); return; } @@ -267,9 +274,9 @@ public class MyApplication extends Application { updateContent = contents[3].substring(contents[3].indexOf(":") + 1); SharedPreUtils spu = SharedPreUtils.getInstance(); spu.putString("lanzousKeyStart", contents[4].substring(contents[4].indexOf(":") + 1)); - if (!StringHelper.isEmpty(downloadLink)){ + if (!StringHelper.isEmpty(downloadLink)) { spu.putString("downloadLink", downloadLink); - }else { + } else { spu.putString("downloadLink", URLCONST.APP_DIR_UR); } String[] updateContents = updateContent.split("/"); @@ -307,14 +314,14 @@ public class MyApplication extends Application { final boolean isForceUpdate, final BookcaseFragment mBookcaseFragment) { //String version = (versionCode / 100 % 10) + "." + (versionCode / 10 % 10) + "." + (versionCode % 10); String cancelTitle; - if(isForceUpdate){ + if (isForceUpdate) { cancelTitle = "退出"; - }else { + } else { cancelTitle = "忽略此版本"; } - if (mBookcaseFragment == null){ - DialogCreator.createCommonDialog(activity, "发现新版本:", message, true, "取消", "立即更新", null, - (dialog, which) -> goDownload(activity, url)); + if (mBookcaseFragment == null) { + DialogCreator.createCommonDialog(activity, "发现新版本:", message, true, "取消", "立即更新", null, + (dialog, which) -> goDownload(activity, url)); 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; if (url == null || "".equals(url)) { downloadLink = URLCONST.APP_DIR_UR; 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 a0ba02f..9b96821 100644 --- a/app/src/main/java/xyz/fycz/myreader/backup/BackupAndRestore.java +++ b/app/src/main/java/xyz/fycz/myreader/backup/BackupAndRestore.java @@ -48,20 +48,7 @@ public class BackupAndRestore { e.printStackTrace(); return false; } finally { - if (bw != null) { - try { - bw.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if (oos != null) { - try { - oos.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } + IOUtils.close(bw, oos); } } 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 791a803..8309c6c 100644 --- a/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java @@ -126,5 +126,4 @@ public class BaseActivity extends AppCompatActivity { return mInputMethodManager; } - } diff --git a/app/src/main/java/xyz/fycz/myreader/base/BaseListAdapter.java b/app/src/main/java/xyz/fycz/myreader/base/adapter/BaseListAdapter.java similarity index 97% rename from app/src/main/java/xyz/fycz/myreader/base/BaseListAdapter.java rename to app/src/main/java/xyz/fycz/myreader/base/adapter/BaseListAdapter.java index 20f71a5..59bedb5 100644 --- a/app/src/main/java/xyz/fycz/myreader/base/BaseListAdapter.java +++ b/app/src/main/java/xyz/fycz/myreader/base/adapter/BaseListAdapter.java @@ -1,9 +1,11 @@ -package xyz.fycz.myreader.base; +package xyz.fycz.myreader.base.adapter; import android.os.Handler; import android.view.View; import android.view.ViewGroup; import androidx.recyclerview.widget.RecyclerView; +import xyz.fycz.myreader.base.BaseViewHolder; +import xyz.fycz.myreader.base.IViewHolder; import java.util.ArrayList; import java.util.Collections; 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 a997a40..3f532c8 100644 --- a/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java +++ b/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java @@ -71,4 +71,6 @@ public class APPCONST { public static final int SETTING_VERSION = 1; public static final String FORMAT_FILE_DATE = "yyyy-MM-dd"; + + public final static String channelIdDownload = "channel_download"; } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/bookinfo/BookDetailedActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/bookinfo/BookDetailedActivity.java new file mode 100644 index 0000000..f88ccec --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ui/bookinfo/BookDetailedActivity.java @@ -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 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) 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) 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("继续阅读"); + } + } + } +} 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 index e01f262..bcc56df 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemActivity.java @@ -148,6 +148,10 @@ public class FileSystemActivity extends BaseTabActivity { true, (dialog, which) -> { //删除选中的文件 mCurFragment.deleteCheckedFiles(); + //改变菜单状态 + changeMenuStatus(); + //改变是否可以全选 + changeCheckedAllStatus(); //提示删除文件成功 TextHelper.showText("删除文件成功"); }, null); 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 index a20b6ea..df8a4d9 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemAdapter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/filesys/FileSystemAdapter.java @@ -1,7 +1,7 @@ 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.greendao.service.BookService; 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 index 3648e59..3417081 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/filesys/LocalBookFragment.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/filesys/LocalBookFragment.java @@ -70,8 +70,12 @@ public class LocalBookFragment extends BaseFileFragment { protected void processLogic() { super.processLogic(); //更新媒体库 - MediaScannerConnection.scanFile(getContext(), new String[]{Environment - .getExternalStorageDirectory().getAbsolutePath()}, new String[]{"text/plain"}, null); + try { + MediaScannerConnection.scanFile(getContext(), new String[]{Environment + .getExternalStorageDirectory().getAbsolutePath()}, new String[]{"text/plain"}, null); + }catch (Exception e){ + e.printStackTrace(); + } MediaStoreHelper.getAllBookFile(getActivity(), (files) -> { if (files.isEmpty()) { 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 a404923..450851b 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 @@ -186,12 +186,7 @@ public abstract class BookcaseAdapter extends DragAdapter { public void onClick(DialogInterface dialog, int which) { selectedIndex = which; } - }).setNegativeButton("取消", (new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - })).setPositiveButton("确定", + }).setNegativeButton("取消", ((dialog, which) -> dialog.dismiss())).setPositiveButton("确定", (dialog, which) -> { switch (selectedIndex) { case 0: @@ -213,7 +208,7 @@ public abstract class BookcaseAdapter extends DragAdapter { } Thread downloadThread = new Thread(() -> { ArrayList chapters = (ArrayList) 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); }).show(); 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 73a5c0d..a869f92 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 @@ -24,6 +24,7 @@ import xyz.fycz.myreader.application.MyApplication; import xyz.fycz.myreader.common.APPCONST; import xyz.fycz.myreader.creator.DialogCreator; 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.read.ReadActivity; import xyz.fycz.myreader.util.StringHelper; @@ -127,7 +128,7 @@ public class BookcaseDetailedAdapter extends BookcaseAdapter { mContext.startActivity(intent); }); viewHolder.ivBookImg.setOnClickListener(v -> { - Intent intent = new Intent(mContext, BookInfoActivity.class); + Intent intent = new Intent(mContext, BookDetailedActivity.class); intent.putExtra(APPCONST.BOOK, book); mContext.startActivity(intent); }); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseFragment.java b/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseFragment.java index dfe0d22..91805db 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseFragment.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/home/bookcase/BookcaseFragment.java @@ -64,6 +64,7 @@ public class BookcaseFragment extends Fragment { public void onDestroyView() { super.onDestroyView(); unbinder.unbind(); + mBookcasePresenter.destroy(); } @Override 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 b2253d7..13ddf6b 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 @@ -2,29 +2,25 @@ package xyz.fycz.myreader.ui.home.bookcase; import android.Manifest; import android.annotation.SuppressLint; -import android.content.DialogInterface; -import android.content.Intent; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.*; +import android.graphics.BitmapFactory; import android.os.Build; import android.os.Handler; import android.os.Message; import android.view.Menu; -import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.PopupMenu; import androidx.appcompat.app.AlertDialog; import androidx.core.app.ActivityCompat; +import androidx.core.app.NotificationCompat; 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.IOException; -import java.io.InputStreamReader; import java.lang.reflect.Method; import java.text.SimpleDateFormat; 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.user.LoginActivity; 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.webapi.CommonApi; -import static xyz.fycz.myreader.application.MyApplication.checkVersionByServer; - public class BookcasePresenter implements BasePresenter { @@ -85,16 +81,26 @@ public class BookcasePresenter implements BasePresenter { return es; } - private String downloadingBook; - private String downloadingChapter; - private boolean isDownloadFinish; - private boolean isStopDownload; - private int downloadProcess; - private PopupMenu pm; + private NotificationUtil notificationUtil;//通知工具类 + private String downloadingBook;//正在下载的书名 + private String downloadingChapter;//正在下载的章节名 + private boolean isDownloadFinish = true;//单本书是否下载完成 + private static boolean isStopDownload = true;//是否停止下载 + 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 = { MyApplication.getmContext().getResources().getString(R.string.menu_backup_backup), MyApplication.getmContext().getResources().getString(R.string.menu_backup_restore), }; + private final String[] webSynMenu = { MyApplication.getmContext().getString(R.string.menu_backup_webBackup), MyApplication.getmContext().getString(R.string.menu_backup_webRestore), @@ -124,6 +130,9 @@ public class BookcasePresenter implements BasePresenter { break; case 4: showErrorLoadingBooks(); + if (MyApplication.isApkInDebug(mMainActivity)) { + downloadAll(false); + } break; case 5: backup(); @@ -135,7 +144,7 @@ public class BookcasePresenter implements BasePresenter { init(); break; case 8: - sendNotification(downloadingBook); + sendNotification(); break; case 9: mBookcaseFragment.getRlDownloadTip().setVisibility(View.GONE); @@ -150,10 +159,8 @@ public class BookcasePresenter implements BasePresenter { MyApplication.runOnUiThread(() -> createMenu()); break; case 12: - mBookcaseFragment.getTvStopDownload().setVisibility(View.GONE); - break; - case 13: - mBookcaseFragment.getTvStopDownload().setVisibility(View.VISIBLE); + TextHelper.showText("正在后台缓存书籍,具体进度可查看通知栏!"); + notificationUtil.requestNotificationPermissionDialog(mMainActivity); break; } } @@ -178,6 +185,10 @@ public class BookcasePresenter implements BasePresenter { if (mSetting.isAutoSyn() && UserService.isLogin()) { synBookcaseToWeb(true); } + + sendDownloadNotification = this::sendNotification; + notificationUtil = NotificationUtil.getInstance(); + getData(); //是否启用下拉刷新(默认启用) 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(); break; case R.id.action_download_all: - DialogCreator.createCommonDialog(mMainActivity, "一键缓存(实验)", - mMainActivity.getString(R.string.all_cathe_tip), true, - (dialog, which) -> downloadAll(), null); + if (!SharedPreUtils.getInstance().getBoolean("isReadDownloadAllTip")) { + DialogCreator.createCommonDialog(mMainActivity, "一键缓存", + mMainActivity.getString(R.string.all_cathe_tip), true, + (dialog, which) -> { + downloadAll(true); + SharedPreUtils.getInstance().putBoolean("isReadDownloadAllTip", true); + }, null); + }else { + downloadAll(true); + } return true; 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()) { TextHelper.showText("无网络连接!"); return; } - mHandler.sendMessage(mHandler.obtainMessage(13)); - isStopDownload = false; + if (isDownloadAllChapters) { + mHandler.sendEmptyMessage(12); + } MyApplication.getApplication().newThread(() -> { - downloadFor: - for (final Book book : mBooks) { - if (BookSource.pinshu.toString().equals(book.getSource()) || "本地书籍".equals(book.getType())) { - continue; + ArrayList needDownloadBooks = new ArrayList<>(); + for (Book book : mBooks) { + if (!BookSource.pinshu.toString().equals(book.getSource()) && !"本地书籍".equals(book.getType())) { + needDownloadBooks.add(book); } + } + downloadFor: + for (final Book book : needDownloadBooks) { isDownloadFinish = false; Thread downloadThread = new Thread(() -> { ArrayList chapters = (ArrayList) mChapterService.findBookAllChapterByBookId(book.getId()); + int end; + if (isDownloadAllChapters) { + end = chapters.size(); + } else { + end = book.getHisttoryChapterNum() + 5; + } addDownload(book, chapters, - book.getHisttoryChapterNum(), chapters.size()); + book.getHisttoryChapterNum(), end, true); }); es.submit(downloadThread); do { try { - Thread.sleep(800); + Thread.sleep(downloadInterval); } catch (InterruptedException e) { e.printStackTrace(); } if (isStopDownload) { - DialogCreator.createTipDialog(mMainActivity, "请等待当前书籍缓存完成后缓存任务将全部停止!"); break downloadFor; } } 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 end */ - public void addDownload(final Book book, final ArrayList mChapters, int begin, int end) { + public void addDownload(final Book book, final ArrayList mChapters, int begin, int end, boolean isDownloadAll) { if ("本地书籍".equals(book.getType())) { TextHelper.showText("《" + book.getName() + "》是本地书籍,不能缓存"); return; @@ -681,54 +714,71 @@ public class BookcasePresenter implements BasePresenter { TextHelper.showText("《" + book.getName() + "》章节目录为空,缓存失败,请刷新后重试"); 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(); final int finalBegin = Math.max(0, begin); final int finalEnd = Math.min(end, mChapters.size()); - final int needCacheChapterNum = finalEnd - finalBegin; - final int[] curCacheChapterNum = {0}; - //final boolean[] isDownloadFinish = new boolean[1]; + needCacheChapterNum = finalEnd - finalBegin; + curCacheChapterNum = 0; + tempCacheChapterNum = 0; + isStopDownload = false; + ArrayList needDownloadChapters = new ArrayList<>(); for (int i = finalBegin; i < finalEnd; i++) { - //isDownloadFinish[0] = false; final Chapter chapter = mChapters.get(i); if (StringHelper.isEmpty(chapter.getContent())) { - getChapterContent(book, chapter, new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - curCacheChapterNum[0]++; - downloadingChapter = chapter.getTitle(); - downloadProcess = curCacheChapterNum[0] * 100 / needCacheChapterNum; - mChapterService.saveOrUpdateChapter(chapter, (String) o); - // isDownloadFinish[0] = true; - mHandler.sendMessage(mHandler.obtainMessage(8)); - } + 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() { + @Override + public void onFinish(Object o, int code) { + downloadingChapter = chapter.getTitle(); + mChapterService.saveOrUpdateChapter(chapter, (String) o); + curCacheChapterNum++; + } - @Override - public void onError(Exception e) { - // isDownloadFinish[0] = true; - curCacheChapterNum[0]++; - downloadProcess = curCacheChapterNum[0] * 100 / needCacheChapterNum; - mHandler.sendMessage(mHandler.obtainMessage(8)); - } - }); - } else { - //isDownloadFinish[0] = true; - curCacheChapterNum[0]++; - downloadProcess = curCacheChapterNum[0] * 100 / needCacheChapterNum; - mHandler.sendMessage(mHandler.obtainMessage(8)); + @Override + public void onError(Exception e) { + curCacheChapterNum++; + } + }); + try { + Thread.sleep(downloadInterval); + } catch (InterruptedException e) { + e.printStackTrace(); } - if (curCacheChapterNum[0] == needCacheChapterNum) { + if (curCacheChapterNum == needCacheChapterNum) { + if (!isDownloadAll) { + isStopDownload = true; + } mHandler.sendMessage(mHandler.obtainMessage(9)); } - /*while (true){ - if (isDownloadFinish[0]){ - break; - } - }*/ if (isStopDownload) { 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())) { chapter.setBookId(mBook.getId()); } - if (!StringHelper.isEmpty(chapter.getContent())) { - if (resultCallback != null) { - resultCallback.onFinish(mChapterService.getChapterCatheContent(chapter), 0); - } + ReadCrawler mReadCrawler = ReadCrawlerUtil.getReadCrawler(mBook.getSource()); + CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler, resultCallback); + } + + /** + * 发送通知 + */ + private void sendNotification() { + if (curCacheChapterNum == needCacheChapterNum) { + mHandler.sendEmptyMessage(9); + notificationUtil.cancelAll(); + return; } else { - ReadCrawler mReadCrawler = ReadCrawlerUtil.getReadCrawler(mBook.getSource()); - CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler, resultCallback); + 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) { - mBookcaseFragment.getPbDownload().setProgress(downloadProcess); - mBookcaseFragment.getTvDownloadTip().setText("正在缓存:" + book + "[" + downloadProcess + "%]"); - if (downloadProcess == 100) { - mHandler.sendMessage(mHandler.obtainMessage(9)); + public static class cancelDownloadReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + //todo 跳转之前要处理的逻辑 + if (CANCEL_ACTION.equals(intent.getAction())) { + isStopDownload = true; + } } } @@ -841,6 +925,19 @@ public class BookcasePresenter implements BasePresenter { public void cancelEdit() { 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{ @Nullable diff --git a/app/src/main/java/xyz/fycz/myreader/ui/home/bookstore/BookStorePresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/home/bookstore/BookStorePresenter.java index cf1bb96..325fd5c 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/home/bookstore/BookStorePresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/home/bookstore/BookStorePresenter.java @@ -15,6 +15,7 @@ import xyz.fycz.myreader.common.APPCONST; import xyz.fycz.myreader.common.URLCONST; import xyz.fycz.myreader.entity.bookstore.BookType; 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.home.MainActivity; import xyz.fycz.myreader.util.TextHelper; @@ -175,7 +176,7 @@ public class BookStorePresenter implements BasePresenter { mBookStoreBookAdapter.setOnItemClickListener(new BookStoreBookAdapter.OnItemClickListener() { @Override 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)); mBookStoreFragment.getActivity().startActivity(intent); } 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 5706418..14767d0 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 @@ -2,11 +2,13 @@ package xyz.fycz.myreader.ui.read; import android.annotation.SuppressLint; import android.app.Dialog; +import android.app.Notification; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Handler; 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.ChapterService; 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.util.BrightUtil; import xyz.fycz.myreader.util.ScreenHelper; import xyz.fycz.myreader.util.StringHelper; 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.webapi.CommonApi; import xyz.fycz.myreader.widget.page.LocalPageLoader; @@ -68,6 +73,7 @@ public class ReadPresenter implements BasePresenter { private ChapterService mChapterService; private BookService mBookService; private BookMarkService mBookMarkService; + private NotificationUtil notificationUtil; private Setting mSetting; private boolean settingChange;//是否是设置改变 @@ -95,6 +101,12 @@ public class ReadPresenter implements BasePresenter { private Runnable keepScreenRunnable;//息屏线程 private Runnable autoPageRunnable;//自动翻页 private Runnable upHpbNextPage;//更新自动翻页进度条 + private Runnable sendDownloadNotification; + private static boolean isStopDownload = true; + + private int tempCacheChapterNum; + private int tempCount; + private String downloadingChapter; private ReadCrawler mReadCrawler; @@ -104,6 +116,8 @@ public class ReadPresenter implements BasePresenter { private int upHpbInterval = 30;//更新翻页进度速度 + private int downloadInterval = 150; + private final CharSequence[] pageMode = { "覆盖", "仿真", "滑动", "滚动", "无动画" }; @@ -156,6 +170,10 @@ public class ReadPresenter implements BasePresenter { case 8: mReadActivity.getPbLoading().setVisibility(View.GONE); break; + case 9: + TextHelper.showText("正在后台缓存书籍,具体进度可查看通知栏!"); + notificationUtil.requestNotificationPermissionDialog(mReadActivity); + break; } } }; @@ -168,20 +186,26 @@ public class ReadPresenter implements BasePresenter { int level = intent.getIntExtra("level", 0); try { mPageLoader.updateBattery(level); - }catch (Exception e){ + } catch (Exception e) { e.printStackTrace(); } } // 监听分钟的变化 else if (Intent.ACTION_TIME_TICK.equals(intent.getAction())) { - mPageLoader.updateTime(); + try { + mPageLoader.updateTime(); + } catch (Exception e) { + e.printStackTrace(); + } } } }; + public ReadPresenter(ReadActivity readActivity) { mReadActivity = readActivity; - mBookService = BookService.getInstance();; + mBookService = BookService.getInstance(); + ; mChapterService = ChapterService.getInstance(); mBookMarkService = BookMarkService.getInstance(); mSetting = SysManager.getSetting(); @@ -197,6 +221,9 @@ public class ReadPresenter implements BasePresenter { upHpbNextPage = this::upHpbNextPage; + sendDownloadNotification = this::sendNotification; + notificationUtil = NotificationUtil.getInstance(); + //注册广播 IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); @@ -393,7 +420,7 @@ public class ReadPresenter implements BasePresenter { Intent intent = new Intent(mReadActivity, FontsActivity.class); mReadActivity.startActivityForResult(intent, APPCONST.REQUEST_FONT); }, this::showPageModeDialog, v -> { - if (mSetting.getPageMode() == PageMode.SCROLL){ + if (mSetting.getPageMode() == PageMode.SCROLL) { TextHelper.showText("滚动暂时不支持自动翻页"); return; } @@ -509,7 +536,7 @@ public class ReadPresenter implements BasePresenter { /** * 初始化监听器 */ - private void initListener(){ + private void initListener() { mReadActivity.getSrlContent().setTouchListener(new PageView.TouchListener() { @Override @@ -523,7 +550,7 @@ public class ReadPresenter implements BasePresenter { if (mPageLoader.getPageStatus() == PageLoader.STATUS_FINISH) { showSettingView(); } - if (autoPage){ + if (autoPage) { autoPageStop(); } } @@ -539,7 +566,7 @@ public class ReadPresenter implements BasePresenter { if (!hasNextPage && endPageTipCount == 3) { mReadActivity.getTvEndPageTip().setVisibility(View.VISIBLE); mHandler.sendMessage(mHandler.obtainMessage(5)); - if (autoPage){ + if (autoPage) { 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()) && !ChapterService.isChapterCached(mBook.getId(), mChapters.get(0).getTitle()))) { if ("本地书籍".equals(mBook.getType())) { - if (!new File(mBook.getChapterUrl()).exists()){ + if (!new File(mBook.getChapterUrl()).exists()) { TextHelper.showText("书籍缓存为空且源文件不存在,书籍加载失败!"); mReadActivity.finish(); return; @@ -729,65 +754,81 @@ public class ReadPresenter implements BasePresenter { TextHelper.showText("无网络连接!"); return; } - new AlertDialog.Builder(mReadActivity) - .setTitle("缓存书籍") - .setSingleChoiceItems(APPCONST.DIALOG_DOWNLOAD, selectedIndex, new DialogInterface.OnClickListener() { - @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) -> { - switch (selectedIndex) { - case 0: - addDownload(tvDownloadProgress, mPageLoader.getChapterPos(), mPageLoader.getChapterPos() + 50); - break; - case 1: - addDownload(tvDownloadProgress, mPageLoader.getChapterPos() - 50, mPageLoader.getChapterPos() + 50); - break; - case 2: - addDownload(tvDownloadProgress, mPageLoader.getChapterPos(), mChapters.size()); - break; - case 3: - addDownload(tvDownloadProgress, 0, mChapters.size()); - break; - } - }).show(); + MyApplication.runOnUiThread(() ->{ + new AlertDialog.Builder(mReadActivity) + .setTitle("缓存书籍") + .setSingleChoiceItems(APPCONST.DIALOG_DOWNLOAD, selectedIndex, (dialog, which) -> selectedIndex = which).setNegativeButton("取消", ((dialog, which) -> dialog.dismiss())).setPositiveButton("确定", + (dialog, which) -> { + switch (selectedIndex) { + case 0: + addDownload(tvDownloadProgress, mPageLoader.getChapterPos(), mPageLoader.getChapterPos() + 50); + break; + case 1: + addDownload(tvDownloadProgress, mPageLoader.getChapterPos() - 50, mPageLoader.getChapterPos() + 50); + break; + case 2: + addDownload(tvDownloadProgress, mPageLoader.getChapterPos(), mChapters.size()); + break; + case 3: + addDownload(tvDownloadProgress, 0, mChapters.size()); + break; + } + }).show(); + }); } 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 finalEnd = Math.min(end, mChapters.size()); needCacheChapterNum = finalEnd - finalBegin; curCacheChapterNum = 0; + isStopDownload = false; + ArrayList needDownloadChapters = new ArrayList<>(); + for (int i = finalBegin; i < finalEnd; i++) { + final Chapter chapter = mChapters.get(i); + 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 (int i = finalBegin; i < finalEnd; i++) { - final Chapter chapter = mChapters.get(i); - if (StringHelper.isEmpty(chapter.getContent())) { - getChapterContent(chapter, new ResultCallback() { - @Override - public void onFinish(Object o, int code) { -// chapter.setContent((String) o); - mChapterService.saveOrUpdateChapter(chapter, (String) o); - curCacheChapterNum++; - mHandler.sendMessage(mHandler.obtainMessage(3, tvDownloadProgress)); - } + for (Chapter chapter : needDownloadChapters) { + getChapterContent(chapter, new ResultCallback() { + @Override + public void onFinish(Object o, int code) { + downloadingChapter = chapter.getTitle(); + mChapterService.saveOrUpdateChapter(chapter, (String) o); + curCacheChapterNum++; + mHandler.sendMessage(mHandler.obtainMessage(3, tvDownloadProgress)); + } - @Override - public void onError(Exception e) { - curCacheChapterNum++; - mHandler.sendMessage(mHandler.obtainMessage(3, tvDownloadProgress)); - } - }); - } else { - curCacheChapterNum++; - mHandler.sendMessage(mHandler.obtainMessage(3, tvDownloadProgress)); + @Override + public void onError(Exception e) { + curCacheChapterNum++; + mHandler.sendMessage(mHandler.obtainMessage(3, tvDownloadProgress)); + } + }); + try { + Thread.sleep(downloadInterval); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (isStopDownload) { + break; } } 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 */ private void addLocalBook(String path) { @@ -1001,7 +1089,7 @@ public class ReadPresenter implements BasePresenter { //判断书籍是否已经添加 Book existsBook = mBookService.findBookByAuthorAndName(book.getName(), book.getAuthor()); - if (book.equals(existsBook)){ + if (book.equals(existsBook)) { mBook = existsBook; return; } @@ -1012,10 +1100,11 @@ public class ReadPresenter implements BasePresenter { /** * 跳转到指定章节的指定页面 + * * @param chapterPos * @param pagePos */ - private void skipToChapterAndPage(final int chapterPos, final int pagePos){ + private void skipToChapterAndPage(final int chapterPos, final int pagePos) { isPrev = false; if (StringHelper.isEmpty(mChapters.get(chapterPos).getContent())) { if ("本地书籍".equals(mBook.getType())) { @@ -1122,10 +1211,16 @@ public class ReadPresenter implements BasePresenter { */ public void onDestroy() { mReadActivity.unregisterReceiver(mReceiver); + mHandler.removeCallbacks(keepScreenRunnable); + mHandler.removeCallbacks(upHpbNextPage); + mHandler.removeCallbacks(autoPageRunnable); + /*mHandler.removeCallbacks(sendDownloadNotification); + notificationUtil.cancelAll(); + MyApplication.getApplication().shutdownThreadPool();*/ if (autoPage) { autoPageStop(); } - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 9; i++) { mHandler.removeMessages(i + 1); } mPageLoader.closeBook(); 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 4e18fe4..4987ce3 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 @@ -22,6 +22,7 @@ import xyz.fycz.myreader.application.MyApplication; import xyz.fycz.myreader.crawler.*; import xyz.fycz.myreader.entity.SearchBookBean; import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap; +import xyz.fycz.myreader.ui.bookinfo.BookDetailedActivity; import xyz.fycz.myreader.ui.bookinfo.BookInfoActivity; import xyz.fycz.myreader.R; import xyz.fycz.myreader.base.BasePresenter; @@ -141,7 +142,7 @@ public class SearchBookPrensenter implements BasePresenter { }); //进入书籍详情页 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)))); mSearchBookActivity.startActivity(intent); }); diff --git a/app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java b/app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java index 4f9d7b6..c98b0a1 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java +++ b/app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java @@ -219,55 +219,51 @@ public class HttpUtil { } public static void sendGetRequest_okHttp(final String address, final HttpCallback callback) { - MyApplication.getApplication().newThread(new Runnable() { - @Override - public void run() { - /* HttpURLConnection connection = null; - try { - URL url = new URL(address); - connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - connection.setRequestProperty("Content-type", "text/html"); - connection.setRequestProperty("Accept-Charset", "gbk"); - connection.setRequestProperty("contentType", "gbk"); - connection.setConnectTimeout(5 * 1000); - connection.setReadTimeout(5 * 1000); - connection.setDoInput(true); - connection.setDoOutput(true); - if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { - Log.e("Http", "网络错误异常!!!!"); - } - InputStream in = connection.getInputStream(); - Log.d("Http", "connection success"); - if (callback != null) { - callback.onFinish(in); - } - } catch (Exception e) { - e.printStackTrace(); - Log.e("Http", e.toString()); - if (callback != null) { - callback.onError(e); - } - } finally { - if (connection != null) { - connection.disconnect(); - } - }*/ - try{ - OkHttpClient client = getOkHttpClient(); - Request request = new Request.Builder() - .addHeader("User-Agent","Mozilla/4.0 (compatible; MSIE 7.0; Windows 7)") - .url(address) - .build(); - Response response = client.newCall(request).execute(); - callback.onFinish(response.body().byteStream()); - }catch(Exception e){ - e.printStackTrace(); - callback.onError(e); - } - } - - }); + MyApplication.getApplication().newThread(() -> { + /* HttpURLConnection connection = null; + try { + URL url = new URL(address); + connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setRequestProperty("Content-type", "text/html"); + connection.setRequestProperty("Accept-Charset", "gbk"); + connection.setRequestProperty("contentType", "gbk"); + connection.setConnectTimeout(5 * 1000); + connection.setReadTimeout(5 * 1000); + connection.setDoInput(true); + connection.setDoOutput(true); + if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { + Log.e("Http", "网络错误异常!!!!"); + } + InputStream in = connection.getInputStream(); + Log.d("Http", "connection success"); + if (callback != null) { + callback.onFinish(in); + } + } catch (Exception e) { + e.printStackTrace(); + Log.e("Http", e.toString()); + if (callback != null) { + callback.onError(e); + } + } finally { + if (connection != null) { + connection.disconnect(); + } + }*/ + try{ + OkHttpClient client = getOkHttpClient(); + Request request = new Request.Builder() + .addHeader("User-Agent","Mozilla/4.0 (compatible; MSIE 7.0; Windows 7)") + .url(address) + .build(); + Response response = client.newCall(request).execute(); + callback.onFinish(response.body().byteStream()); + }catch(Exception e){ + e.printStackTrace(); + callback.onError(e); + } + }); } /** diff --git a/app/src/main/java/xyz/fycz/myreader/util/TextHelper.java b/app/src/main/java/xyz/fycz/myreader/util/TextHelper.java index 07ca922..b4cb59a 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/TextHelper.java +++ b/app/src/main/java/xyz/fycz/myreader/util/TextHelper.java @@ -11,21 +11,11 @@ public class TextHelper { public static void showText(final String text){ - MyApplication.runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(MyApplication.getApplication(),text, Toast.LENGTH_SHORT).show(); - } - }); + MyApplication.runOnUiThread(() -> Toast.makeText(MyApplication.getApplication(),text, Toast.LENGTH_SHORT).show()); } public static void showLongText(final String text){ - MyApplication.runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(MyApplication.getApplication(),text, Toast.LENGTH_LONG).show(); - } - }); + MyApplication.runOnUiThread(() -> Toast.makeText(MyApplication.getApplication(),text, Toast.LENGTH_LONG).show()); } } \ No newline at end of file diff --git a/app/src/main/java/xyz/fycz/myreader/util/notification/NotificationClickReceiver.java b/app/src/main/java/xyz/fycz/myreader/util/notification/NotificationClickReceiver.java new file mode 100644 index 0000000..32a5b30 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/util/notification/NotificationClickReceiver.java @@ -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(); + } + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/util/notification/NotificationUtil.java b/app/src/main/java/xyz/fycz/myreader/util/notification/NotificationUtil.java new file mode 100644 index 0000000..0e9b688 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/util/notification/NotificationUtil.java @@ -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; + } +} diff --git a/app/src/main/res/drawable/ic_vector_add_bookcase.xml b/app/src/main/res/drawable/ic_vector_add_bookcase.xml new file mode 100644 index 0000000..7b44c5b --- /dev/null +++ b/app/src/main/res/drawable/ic_vector_add_bookcase.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vector_book_read.xml b/app/src/main/res/drawable/ic_vector_book_read.xml new file mode 100644 index 0000000..d2d6fde --- /dev/null +++ b/app/src/main/res/drawable/ic_vector_book_read.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/selector_pwd.xml b/app/src/main/res/drawable/selector_pwd.xml deleted file mode 100644 index f3b6764..0000000 --- a/app/src/main/res/drawable/selector_pwd.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_book_detail.xml b/app/src/main/res/layout/activity_book_detail.xml new file mode 100644 index 0000000..4323636 --- /dev/null +++ b/app/src/main/res/layout/activity_book_detail.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_book_detail_bottom.xml b/app/src/main/res/layout/layout_book_detail_bottom.xml new file mode 100644 index 0000000..48c7b0d --- /dev/null +++ b/app/src/main/res/layout/layout_book_detail_bottom.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_book_detail_content.xml b/app/src/main/res/layout/layout_book_detail_content.xml new file mode 100644 index 0000000..7282919 --- /dev/null +++ b/app/src/main/res/layout/layout_book_detail_content.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_book_detail_header.xml b/app/src/main/res/layout/layout_book_detail_header.xml new file mode 100644 index 0000000..b50b250 --- /dev/null +++ b/app/src/main/res/layout/layout_book_detail_header.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_book_detail.xml b/app/src/main/res/menu/menu_book_detail.xml new file mode 100644 index 0000000..96d4441 --- /dev/null +++ b/app/src/main/res/menu/menu_book_detail.xml @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/app/src/main/res/mipmap-xhdpi/ic_menu_exchange.png b/app/src/main/res/mipmap-xhdpi/ic_menu_exchange.png new file mode 100644 index 0000000000000000000000000000000000000000..cf9e526836e27611d08f494b62749742fa4cd8fb GIT binary patch literal 659 zcmV;E0&M+>P);-0}oCA`& zS^!ebP6Deki#MnH4YW!6*>J%2PT*YW$>+e!j36t4nMD~SEpIqrdkXLhm;t;6woAH| zOWgK0-~uon_yTN}bgvB11DsES8zkMyDBcNN0wyH!PDyu*{KF-%eH1tXe3mplqxgK_ zF)#`E3T!QlmmTN>_5(wb7Gwm7I)|HWK)iWNSqleh0Z8u70j5cMR<*7jpxI5^H=-p2 zJ(4c|3xFYD9?&Q0;26M|1OJT!J5nnil+=-{5mn`}-FgaAgIJPUaeR!TJue16NqV0X zXjFi*i0yXZ3h*6RC+Sfx2-_dhU~)pz;g*RgiC+dL1K)t9l3wQku)Pp?k#g;mbZP{E z?e)ObB>n^Fk`zv43EO9Z-6`3!aJD=VtpTnBQ-NPVx1@m_%C;ltA>croL;EGg4Dxp< zwH+f^TqGi5VttT9JmpxMT#aRH@)gDXpy&e=Q@zJ!6s<}XU&39I;*v5PPA}D#mEH4j*sU;+(zL=<(`Vd$IStx3OVu`5~qJ*fVNQw^%Ug#n5 zo*H3vp%)R+P_qZ62)YP9lpvO-{}@?YcE@wRy*=m1H*nbOvuDlx)|$0uW{Fl7wX%Oo z0oZN}Ob12+z0&Ur@CWcS@P(xB>zPc80JeJq?*Oxa?gb8h1vUX6NxD`RrYeB#hk!-E z65x@Xv44TzfN<6g=m)f~aCQlpCuvU^h`Ru64+IVX!*T`=13Th>N&n;x+a3YD0=x!v zP2UVGmGof|#Qy+nM~uJ9&H5YQElFpJ4A~Cn%YX$yo20*1QiQOk+ySutC~y*pxZebp zmzhDOW!sa0PjjJpRno3n1bKktz^J4+SJLL9S=ah*`}sH-Ks(?DFkI5vYWlYUY`+K` zNeZ7xnpv;&Y@F?>z`pc;RMNzj0J7Qt2@H@F&xXbJAs|8$GDgyOl}2TI2=GJtut?In zhCOFT!G01ronALedb=9HQeb8J&_`07^QMXI00@wak{+uD@Fg$~I4@~vqn>TQ49t$K zC~3Ft<-m$$x3{F(WY%bY3(POmlBHue1K5)5px>3Wrql{#O17T{J_mZ_27Z>B z(t}l+(%9i+fjPiaX-urzdy-ZctzNCRyl)D@3l#<`+w%Uv!xcKwvd@+jophlTT}+}? zi*ovGj&-FP4RniZX~00000NkvXXu0mjfaQ2)E literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/pwd_gone.png b/app/src/main/res/mipmap-xhdpi/pwd_gone.png deleted file mode 100644 index 2977fc053ba0d97c660271bba02d4a7181159033..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 920 zcmV;J184kFNk&GH0{{S5MM6+kP&il$0000G0000!001Tc06|PpNFM_L00E%3ZQCI^ zUeY}_j@asWZ7b&5wr$(CZQJ&4Y&)sA;*2U&b$#fr=dJFDm;fc;`Sj=iT^SOt{P+3O zJ1f`QU;01x|1WKyOWVB~kkOwud77UF!K>TlO6n8R=RR;~8>P27^ntmgf0D|D>W2fM z;h8;T-{YAEAb7YsYeSX-o9}ET_f~gq29^%VPQ?pA_)=RX+xDdpc%dQ-+sy!`?ylr_ zp9;*_F0o_lp!xP98GE1e;Pa1Ou}?vqif(U)x?_{cy94m{SA5=ylgYf|=870;e-F4L zFM;BBfS+3_zRRCjmmL(RRzCyp6gyS@2s~S;`0ZB!3m!V|kVCKk7YI&@Q{l6~k5vve zV<5a##&*kK{h0;Ic;IB%HXu&AG=woVrrI$O93$&ju%Nr*%fEybH3l6Mz?fP?P(MKC&*3)N4*)B#rHySxJ@Ira2lc25tyUM`vj!yH;} zCTj1}+XJtsdU~*S50|T9N|Eg;_(qOt1o|Wz&0^vPHN_eknK;wh0 z5^MEfBQR|*C0}$?9kBKB&WUt>d@E3QQ<1XT;q?fJzCEJMq3nonBOrRcgR0001r_qquH diff --git a/app/src/main/res/mipmap-xhdpi/pwd_visiable.png b/app/src/main/res/mipmap-xhdpi/pwd_visiable.png deleted file mode 100644 index a2cbc10ae73372438245c89e61fee2f179eaa411..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1428 zcmV;F1#9|JNk&GD1pok7MM6+kP&il$0000G0000!001Tc06|PpNFM_L00E%3ZQCI^ zUeY}_j@asWZ7b&5wr$(CZQJ&4Y&)sA;*2U&b$#fr=dJFDm;fc;`Sj=iT^SOt{P+3O zJ1f`QU;01x|1WKyOWVB~kkOwud77UF!K>TlO6n8R=RR;~8>P27^ntmgf0D|D>W2fM z;h8;T-{YAEAb7YsYeSX-o9}ET_f~gq29^%VPQ?pA_)=RX+xDdpc%dQ-+sy!`?ylr_ zp9;*_F0o_lp!xP98GE1e;Pa1Ou}?vqif(U)x?_{cy94m{SA5=ylgYf|=870;e-F4L zFM;BBfS+3_zRRCjmmL(RRzCyp6gyS@2s~S;`0ZB!3m!V|kVCKk7YI&@Q{l6~k5vve zV<5a##&*kK{h0;Ic;IB%HXu&AG=woVrrI$O93$&ju%Nr*%fEybH3l6Mz?fP?P(MKC&*3)N4*)B#rHySxJ@Ira2lc25tyUM`vj!yH;} zCTj1}+XJtsdU~*S50|T9N|Eg;_(qOt1o|Wz&0^vPHN_eknK;wh0 z5^MEfBQR|*C0}$?9kBKB&WUt>d@E3QQ<1XT;q?fJzCEJMq3nonBOrRcgR#x?A){nFn0Z-yGaFtJ+8@mu$KXJI&=?BWF!nLgLXaIFdm^1%H#~0Ygq3MyZCySfRisIDT=q^7I z0^vGwC?&>#4NjJ!DgF-Wp>=-8;Dv=>4Pd>96U61Rh~6HoTXw))oW;LGh5!DQcFcMY zZ8csGkn}E*8t@=x`R*f^4gRvX8ZuGPWUU*XOu&9^(-wLC63Y<@;?6j;GOI z$%b$sPijzt)wKW1B9gG-pmc3@RfcOrX4=WTcSV-gwme7sKmT2;$;++wUY;=j>BG0_XWO{ew i##{COP=Dv|2mkL4ek*xASQ7l!*_E^)hq{DJ0001>LcBo$ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e72bf00..fa1572f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,7 +38,7 @@ 免责声明 声明:小说数据全部从网络获取,软件不提供或保存任何小说,详情请参考免责声明: - "说明:本功能属于实验功能,书籍缓存的章节是从历史阅读章节到最新章节!\n\n存在的问题:\n1、书源为品书网的书籍因网站原因无法一键缓存!\n\n2、部分书籍缓存时会卡在99%的位置\n解决方案:点击停止按钮直接进入下一本书籍缓存/重新点击一键缓存!\n\n3、书籍缓存时进入阅读时章节加载可能变慢!\n\n确定要一键缓存?" + "说明:1、书籍缓存的章节是从历史阅读章节到最新章节!\n\n2、书源为品书网的书籍因网站原因无法一键缓存!\n\n确定要一键缓存?" 移至顶部 @@ -50,7 +50,7 @@ 切换布局 添加本地 同步书架 - 一键缓存(实验) + 一键缓存 备份/恢复 关于软件 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 44c4f1c..bcc77db 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -91,7 +91,44 @@ @color/divider_common 0.5dp + + + + + + + + diff --git a/app/version_code.properties b/app/version_code.properties index 78e0b2d..9a7898c 100644 --- a/app/version_code.properties +++ b/app/version_code.properties @@ -1,2 +1,2 @@ -#Wed Aug 12 21:27:50 CST 2020 -VERSION_CODE=141 +#Sat Aug 15 23:51:09 CST 2020 +VERSION_CODE=142