From 9ffec429a7e62e6a285e185decb6999e427b3327 Mon Sep 17 00:00:00 2001
From: fengyuecanzhu <1021300691@qq.com>
Date: Sun, 10 Jan 2021 09:49:09 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=A7=81=E5=AF=86=E4=B9=A6?=
=?UTF-8?q?=E6=9E=B6?=
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 | 1 +
app/src/main/AndroidManifest.xml | 3 +-
app/src/main/assets/updatelog.fy | 13 +-
.../xyz/fycz/myreader/base/BaseActivity.java | 2 +-
.../greendao/service/BookGroupService.java | 30 +
.../greendao/service/BookService.java | 37 +-
.../fycz/myreader/model/storage/WebDavHelp.kt | 6 +-
.../ui/activity/BookDetailedActivity.java | 14 +
.../ui/activity/FileSystemActivity.java | 11 +-
.../myreader/ui/activity/MainActivity.java | 62 ++
.../ui/activity/MoreSettingActivity.java | 131 ++-
.../myreader/ui/activity/ReadActivity.java | 11 +-
.../myreader/ui/activity/SplashActivity.java | 25 +-
.../ui/adapter/FileSystemAdapter.java | 8 +-
.../myreader/ui/dialog/BookGroupDialog.java | 233 ++++++
.../myreader/ui/dialog/DialogCreator.java | 13 +-
.../myreader/ui/dialog/FingerprintDialog.java | 176 ++++
.../myreader/ui/dialog/MyAlertDialog.java | 160 +++-
.../ui/fragment/FileCategoryFragment.java | 28 +-
.../ui/fragment/PrivateBooksFragment.java | 220 +++++
.../WebDavFragment.java} | 83 +-
.../ui/presenter/BookcasePresenter.java | 75 +-
.../myreader/util/utils/FingerprintUtils.java | 80 ++
.../main/res/drawable-xhdpi/ic_fp_40px.png | Bin 0 -> 18565 bytes
.../main/res/layout/activity_more_setting.xml | 786 ++++++++++--------
.../main/res/layout/fingerprint_dialog.xml | 77 ++
.../res/layout/fragment_private_bookcase.xml | 114 +++
...etting.xml => fragment_webdav_setting.xml} | 43 +-
app/src/main/res/menu/menu_book_detail.xml | 3 +-
app/src/main/res/values/strings.xml | 18 +-
app/version_code.properties | 4 +-
32 files changed, 1964 insertions(+), 503 deletions(-)
create mode 100644 app/src/main/java/xyz/fycz/myreader/ui/dialog/BookGroupDialog.java
create mode 100644 app/src/main/java/xyz/fycz/myreader/ui/dialog/FingerprintDialog.java
create mode 100644 app/src/main/java/xyz/fycz/myreader/ui/fragment/PrivateBooksFragment.java
rename app/src/main/java/xyz/fycz/myreader/ui/{activity/WebDavSettingActivity.java => fragment/WebDavFragment.java} (68%)
create mode 100644 app/src/main/java/xyz/fycz/myreader/util/utils/FingerprintUtils.java
create mode 100644 app/src/main/res/drawable-xhdpi/ic_fp_40px.png
create mode 100644 app/src/main/res/layout/fingerprint_dialog.xml
create mode 100644 app/src/main/res/layout/fragment_private_bookcase.xml
rename app/src/main/res/layout/{activity_webdav_setting.xml => fragment_webdav_setting.xml} (77%)
diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser
index bdb9dafd9f32a4ea496a76d54288089d7f523fd3..a93c5b7d2299f655cb0e39b10152690b0ab58e63 100644
GIT binary patch
delta 35
tcmV+;0NnqX1epYom;~igsDqK5cM!)wmUovO^rML3wu|1@Coq#E0rn~(5k3F_
delta 35
tcmV+;0NnqX1epYom;~aVZa9&gcMxmHtLiuTfk8VqT(}g#QOlDf0rnh^5FP*k
diff --git a/app/build.gradle b/app/build.gradle
index cf11245..1261006 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -99,6 +99,7 @@ android {
}
productFlavors {
}
+
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b864072..80471be 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,8 @@
+
+
@@ -102,7 +104,6 @@
-
diff --git a/app/src/main/assets/updatelog.fy b/app/src/main/assets/updatelog.fy
index a947923..f486693 100644
--- a/app/src/main/assets/updatelog.fy
+++ b/app/src/main/assets/updatelog.fy
@@ -1,8 +1,13 @@
-2020.01.08
+2021.01.10
+风月读书v1.7.9
+1、修复私密书架相关bug
+
+2021.01.09
风月读书v1.7.8
-1、新增建议反馈
-2、修复部分已知bug
-3、本地书籍支持匹配数字章节
+1、新增私密书架(设置隐私密码后开启,支持指纹解锁,开启后可以隐藏书籍)
+2、新增建议反馈
+3、修复部分已知bug
+4、本地书籍支持匹配数字章节
2020.12.12
风月读书v1.20.121218
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 e5a1abb..945aef0 100644
--- a/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java
+++ b/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java
@@ -38,7 +38,7 @@ public abstract class BaseActivity extends AppCompatActivity {
protected CompositeDisposable mDisposable;
//ButterKnife
- private Toolbar mToolbar;
+ protected Toolbar mToolbar;
private Unbinder unbinder;
diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/service/BookGroupService.java b/app/src/main/java/xyz/fycz/myreader/greendao/service/BookGroupService.java
index 388776e..42d5df9 100644
--- a/app/src/main/java/xyz/fycz/myreader/greendao/service/BookGroupService.java
+++ b/app/src/main/java/xyz/fycz/myreader/greendao/service/BookGroupService.java
@@ -1,9 +1,13 @@
package xyz.fycz.myreader.greendao.service;
+import xyz.fycz.myreader.R;
+import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.greendao.GreenDaoManager;
import xyz.fycz.myreader.greendao.entity.BookGroup;
import xyz.fycz.myreader.greendao.gen.BookGroupDao;
import xyz.fycz.myreader.greendao.gen.BookMarkDao;
+import xyz.fycz.myreader.ui.activity.SplashActivity;
+import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.StringHelper;
import java.util.List;
@@ -67,6 +71,32 @@ public class BookGroupService extends BaseService{
deleteEntity(bookGroup);
}
+ public void deleteGroupById(String id){
+ GreenDaoManager.getInstance().getSession().getBookGroupDao().deleteByKey(id);
+ }
+
+ public void createPrivateGroup(){
+ BookGroup bookGroup = new BookGroup();
+ bookGroup.setName("私密书架");
+ addBookGroup(bookGroup);
+ SharedPreUtils.getInstance().putString("privateGroupId", bookGroup.getId());
+ }
+
+ public void deletePrivateGroup(){
+ String privateGroupId = SharedPreUtils.getInstance().getString("privateGroupId");
+ deleteGroupById(privateGroupId);
+ BookService.getInstance().deleteBooksByGroupId(privateGroupId);
+ }
+
+ /**
+ * 当前是否为私密书架
+ * @return
+ */
+ public boolean curGroupIsPrivate(){
+ String curBookGroupId = SharedPreUtils.getInstance().getString(MyApplication.getmContext().getString(R.string.curBookGroupId), "");
+ String privateGroupId = SharedPreUtils.getInstance().getString("privateGroupId");
+ return !StringHelper.isEmpty(curBookGroupId) && curBookGroupId.equals(privateGroupId);
+ }
private int countBookGroup(){
return (int) GreenDaoManager.getInstance().getSession().getBookGroupDao()
diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java b/app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java
index 30b56e2..c3c4f5e 100644
--- a/app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java
+++ b/app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java
@@ -77,6 +77,21 @@ public class BookService extends BaseService {
.list();
}
+ /**
+ * 获取所有的书
+ *
+ * @return
+ */
+ public List getAllBooksNoHide() {
+ List oldBooks = getAllBooks();
+ List newBooks = new ArrayList<>();
+ String privateGroupId = SharedPreUtils.getInstance().getString("privateGroupId");
+ for (Book book : oldBooks){
+ if (StringHelper.isEmpty(book.getGroupId()) || !privateGroupId.equals(book.getGroupId())) newBooks.add(book);
+ }
+ return newBooks;
+ }
+
/**
* 获取特定分组的书
*
@@ -84,7 +99,7 @@ public class BookService extends BaseService {
*/
public List getGroupBooks(String groupId) {
if (StringHelper.isEmpty(groupId)){
- return getAllBooks();
+ return getAllBooksNoHide();
}
return GreenDaoManager.getInstance().getSession().getBookDao()
.queryBuilder()
@@ -99,8 +114,6 @@ public class BookService extends BaseService {
* @param book
*/
public void addBook(Book book) {
-// book.setSortCode(countBookTotalNum() + 1);
-
book.setSortCode(0);
book.setGroupSort(0);
book.setGroupId(SharedPreUtils.getInstance().getString(MyApplication.getmContext().getString(R.string.curBookGroupId), ""));
@@ -110,6 +123,15 @@ public class BookService extends BaseService {
addEntity(book);
}
+ public void addBookNoGroup(Book book) {
+ book.setSortCode(0);
+ book.setGroupSort(0);
+ if (StringHelper.isEmpty(book.getId())) {
+ book.setId(StringHelper.getStringRandom(25));
+ }
+ addEntity(book);
+ }
+
/**
* 批量添加书籍
*
@@ -198,6 +220,15 @@ public class BookService extends BaseService {
}
}
+ /**
+ * 通过分组id删除书
+ */
+ public void deleteBooksByGroupId(String groupId) {
+ for (Book book : getGroupBooks(groupId)) {
+ deleteBook(book);
+ }
+ }
+
/**
* 查询书籍总数
*
diff --git a/app/src/main/java/xyz/fycz/myreader/model/storage/WebDavHelp.kt b/app/src/main/java/xyz/fycz/myreader/model/storage/WebDavHelp.kt
index 0d1e423..e8fb03a 100644
--- a/app/src/main/java/xyz/fycz/myreader/model/storage/WebDavHelp.kt
+++ b/app/src/main/java/xyz/fycz/myreader/model/storage/WebDavHelp.kt
@@ -53,9 +53,9 @@ object WebDavHelp {
if (initWebDav()) {
var files = WebDav(url + "FYReader/").listFiles()
files = files.reversed()
- //for (index: Int in 0 until min(10, files.size)) {
- for (element in files) {
- element.displayName?.let {
+ val max = SharedPreUtils.getInstance().getInt("restoreNum", 30)
+ for (index: Int in 0 until min(max, files.size)) {
+ files[index].displayName?.let {
names.add(it)
}
}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
index fadf51a..a3634af 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
@@ -49,6 +49,7 @@ import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.base.observer.MySingleObserver;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.entity.SharedBook;
+import xyz.fycz.myreader.ui.dialog.BookGroupDialog;
import xyz.fycz.myreader.util.IOUtils;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.utils.BitmapUtil;
@@ -130,6 +131,7 @@ public class BookDetailedActivity extends BaseActivity {
private boolean isCollected;
private SourceExchangeDialog mSourceDialog;
private int sourceIndex;
+ private BookGroupDialog mBookGroupDia;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@@ -181,6 +183,7 @@ public class BookDetailedActivity extends BaseActivity {
if (isCollected) {
mChapters = (ArrayList) mChapterService.findBookAllChapterByBookId(mBook.getId());
}
+ mBookGroupDia = new BookGroupDialog(this);
}
@Override
@@ -577,6 +580,17 @@ public class BookDetailedActivity extends BaseActivity {
startActivity(intent);
break;
case R.id.action_group_setting:
+ mBookGroupDia.addGroup(mBook, new BookGroupDialog.OnGroup() {
+ @Override
+ public void change() {
+
+ }
+
+ @Override
+ public void addGroup() {
+
+ }
+ });
break;
default:
break;
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/FileSystemActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/FileSystemActivity.java
index a526a9e..9a0ab9e 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/activity/FileSystemActivity.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/FileSystemActivity.java
@@ -82,7 +82,7 @@ public class FileSystemActivity extends BaseTabActivity {
protected void setUpToolbar(Toolbar toolbar) {
super.setUpToolbar(toolbar);
setStatusBarColor(R.color.colorPrimary, true);
- getSupportActionBar().setTitle("添加本地");
+ getSupportActionBar().setTitle("本地导入");
}
@Override
@@ -171,6 +171,15 @@ public class FileSystemActivity extends BaseTabActivity {
mCurFragment = mLocalFragment;
}
+
+ @Override
+ public void onBackPressed() {
+ if (mCurFragment == mCategoryFragment){
+ if (mCategoryFragment.backLast()) return;
+ }
+ super.onBackPressed();
+ }
+
/**
* 将文件转换成CollBook
* @param files:需要加载的文件列表
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java
index 36b6ccd..3bb5fdd 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java
@@ -6,13 +6,18 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
+import android.text.InputType;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.View;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.widget.ActionMenuView;
+import androidx.appcompat.widget.AppCompatTextView;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
@@ -30,13 +35,19 @@ import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.entity.SharedBook;
import xyz.fycz.myreader.greendao.entity.Book;
+import xyz.fycz.myreader.greendao.entity.BookGroup;
+import xyz.fycz.myreader.greendao.service.BookGroupService;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
+import xyz.fycz.myreader.ui.dialog.FingerprintDialog;
+import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.ui.fragment.BookcaseFragment;
import xyz.fycz.myreader.ui.fragment.FindFragment;
import xyz.fycz.myreader.ui.fragment.MineFragment;
+import xyz.fycz.myreader.util.CyptoUtils;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.ToastUtils;
+import xyz.fycz.myreader.util.utils.FingerprintUtils;
import xyz.fycz.myreader.util.utils.GsonExtensionsKt;
import java.io.File;
@@ -70,6 +81,16 @@ public class MainActivity extends BaseActivity {
return R.layout.activity_main;
}
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ boolean startFromSplash = getIntent().getBooleanExtra("startFromSplash", false);
+ if (!startFromSplash && BookGroupService.getInstance().curGroupIsPrivate()){
+ SharedPreUtils.getInstance().putString(getString(R.string.curBookGroupId), "");
+ SharedPreUtils.getInstance().putString(getString(R.string.curBookGroupName), "");
+ }
+ super.onCreate(savedInstanceState);
+ }
+
@Override
protected void setUpToolbar(Toolbar toolbar) {
super.setUpToolbar(toolbar);
@@ -118,6 +139,15 @@ public class MainActivity extends BaseActivity {
@Override
protected void initClick() {
super.initClick();
+
+ mToolbar.setOnLongClickListener(v -> {
+ if (viewPagerMain.getCurrentItem() == 0 && !BookGroupService.getInstance().curGroupIsPrivate()){
+ goPrivateBookcase();
+ return true;
+ }
+ return false;
+ });
+
//BottomNavigationView 点击事件监听
bottomNavigation.setOnNavigationItemSelectedListener(menuItem -> {
int menuId = menuItem.getItemId();
@@ -292,6 +322,16 @@ public class MainActivity extends BaseActivity {
@Override
protected void onResume() {
super.onResume();
+ String curBookGroupId = SharedPreUtils.getInstance().getString(this.getString(R.string.curBookGroupId), "");
+ BookGroup bookGroup = BookGroupService.getInstance().getGroupById(curBookGroupId);
+ if (bookGroup == null) {
+ groupName = "";
+ }else {
+ groupName = bookGroup.getName();
+ }
+ if (viewPagerMain.getCurrentItem() == 0){
+ getSupportActionBar().setSubtitle(groupName);
+ }
// MyApplication.checkVersionByServer(this);
}
@@ -424,6 +464,28 @@ public class MainActivity extends BaseActivity {
}
}
+ private void goPrivateBookcase(){
+ MyAlertDialog.showPrivateVerifyDia(this, needGoTo -> {
+ if (needGoTo) showPrivateBooks();
+ });
+ }
+
+ /**
+ * 显示私密书架
+ */
+ private void showPrivateBooks(){
+ BookGroup bookGroup = BookGroupService.getInstance().
+ getGroupById(SharedPreUtils.getInstance().getString("privateGroupId"));
+ groupName = bookGroup.getName();
+ SharedPreUtils.getInstance().putString(getString(R.string.curBookGroupId), bookGroup.getId());
+ SharedPreUtils.getInstance().putString(getString(R.string.curBookGroupName), groupName);
+ getSupportActionBar().setSubtitle(groupName);
+ if (mBookcaseFragment.isRecreate()) {
+ reLoadFragment();
+ }
+ mBookcaseFragment.onResume();
+ }
+
public interface OnGroupChangeListener {
void onChange();
}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/MoreSettingActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/MoreSettingActivity.java
index b83bc83..b696d9a 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/activity/MoreSettingActivity.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/MoreSettingActivity.java
@@ -3,29 +3,43 @@ package xyz.fycz.myreader.ui.activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
+import android.text.InputType;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.View;
import android.widget.*;
+
+import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SwitchCompat;
import androidx.appcompat.widget.Toolbar;
+import androidx.fragment.app.FragmentTransaction;
+
import butterknife.BindView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BaseActivity;
+import xyz.fycz.myreader.base.BaseFragment;
import xyz.fycz.myreader.common.APPCONST;
+import xyz.fycz.myreader.greendao.service.BookGroupService;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
+import xyz.fycz.myreader.ui.dialog.FingerprintDialog;
import xyz.fycz.myreader.ui.dialog.MultiChoiceDialog;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.entity.Setting;
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.fragment.PrivateBooksFragment;
+import xyz.fycz.myreader.ui.fragment.WebDavFragment;
+import xyz.fycz.myreader.util.CyptoUtils;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.ToastUtils;
+import xyz.fycz.myreader.util.utils.FingerprintUtils;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import java.io.File;
@@ -41,6 +55,8 @@ import static xyz.fycz.myreader.common.APPCONST.BOOK_CACHE_PATH;
*/
public class MoreSettingActivity extends BaseActivity {
+ @BindView(R.id.sv_content)
+ ScrollView svContent;
@BindView(R.id.ll_webdav)
LinearLayout mLlWebdav;
@BindView(R.id.rl_volume)
@@ -75,6 +91,8 @@ public class MoreSettingActivity extends BaseActivity {
LinearLayout mLlBookSort;
@BindView(R.id.tv_book_sort)
TextView mTvBookSort;
+ @BindView(R.id.rl_private_bookcase)
+ RelativeLayout mRlPrivateBookcase;
@BindView(R.id.ll_close_refresh)
LinearLayout mLlCloseRefresh;
@BindView(R.id.ll_disable_source)
@@ -134,6 +152,10 @@ public class MoreSettingActivity extends BaseActivity {
//选择一键缓存书籍对话框
private AlertDialog mDownloadAllDia;
+ private WebDavFragment mWebDavFragment;
+ private PrivateBooksFragment mPrivateBooksFragment;
+
+ private BaseFragment curFragment;
@Override
protected int getContentId() {
@@ -163,16 +185,28 @@ public class MoreSettingActivity extends BaseActivity {
protected void setUpToolbar(Toolbar toolbar) {
super.setUpToolbar(toolbar);
setStatusBarColor(R.color.colorPrimary, true);
- getSupportActionBar().setTitle("设置");
+ setUpToolbar();
+ }
+
+ private void setUpToolbar() {
+ if (curFragment == null) {
+ getSupportActionBar().setTitle("设置");
+ } else if (curFragment == mWebDavFragment) {
+ getSupportActionBar().setTitle(getString(R.string.webdav_setting));
+ invalidateOptionsMenu();
+ }else if (curFragment == mPrivateBooksFragment){
+ getSupportActionBar().setTitle(getString(R.string.private_bookcase));
+ invalidateOptionsMenu();
+ }
}
@Override
protected void initWidget() {
super.initWidget();
initSwitchStatus();
- if (sortStyle == 1){
+ if (sortStyle == 1) {
mTvBookSort.setText(getString(R.string.time_sort));
- }else if (sortStyle == 2){
+ } else if (sortStyle == 2) {
mTvBookSort.setText(getString(R.string.book_name_sort));
}
if (isMatchChapter) {
@@ -183,6 +217,33 @@ public class MoreSettingActivity extends BaseActivity {
mTvThreadNum.setText(getString(R.string.cur_thread_num, threadNum));
}
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.webdav_help, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if (curFragment == null) {
+ menu.findItem(R.id.action_tip).setVisible(false);
+ } else {
+ menu.findItem(R.id.action_tip).setVisible(true);
+ }
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ if (item.getItemId() == R.id.action_tip) {
+ if (curFragment == mWebDavFragment) {
+ DialogCreator.createAssetTipDialog(this, "如何使用WebDav进行云备份?", "webdavhelp.fy");
+ }else if(curFragment == mPrivateBooksFragment){
+ DialogCreator.createTipDialog(this, "关于私密书架", getString(R.string.private_bookcase_tip));
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
private void initSwitchStatus() {
mScVolume.setChecked(isVolumeTurnPage);
@@ -197,7 +258,19 @@ public class MoreSettingActivity extends BaseActivity {
@Override
protected void initClick() {
super.initClick();
- mLlWebdav.setOnClickListener(v -> startActivity(WebDavSettingActivity.class));
+ mLlWebdav.setOnClickListener(v -> {
+ svContent.setVisibility(View.GONE);
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ if (mWebDavFragment == null) {
+ mWebDavFragment = new WebDavFragment();
+ ft.add(R.id.ll_content, mWebDavFragment);
+ } else {
+ ft.show(mWebDavFragment);
+ }
+ ft.commit();
+ curFragment = mWebDavFragment;
+ setUpToolbar();
+ });
mRlVolume.setOnClickListener(
(v) -> {
@@ -269,21 +342,27 @@ public class MoreSettingActivity extends BaseActivity {
sortStyle = which;
mSetting.setSortStyle(sortStyle);
SysManager.saveSetting(mSetting);
- if (sortStyle == 0){
+ if (sortStyle == 0) {
mTvBookSort.setText(getString(R.string.manual_sort));
- if (!SharedPreUtils.getInstance().getBoolean("manualSortTip")){
+ if (!SharedPreUtils.getInstance().getBoolean("manualSortTip")) {
DialogCreator.createTipDialog(this, "可在书架编辑状态下长按移动书籍进行排序!");
SharedPreUtils.getInstance().putBoolean("manualSortTip", true);
}
- }else if (sortStyle == 1){
+ } else if (sortStyle == 1) {
mTvBookSort.setText(getString(R.string.time_sort));
- }else if (sortStyle == 2){
+ } else if (sortStyle == 2) {
mTvBookSort.setText(getString(R.string.book_name_sort));
}
dialog.dismiss();
}).setNegativeButton("取消", null).show();
});
+ mRlPrivateBookcase.setOnClickListener(v -> {
+ MyAlertDialog.showPrivateVerifyDia(this, needGoTo -> {
+ showPrivateBooksFragment();
+ });
+ });
+
mRlAutoRefresh.setOnClickListener(
(v) -> {
if (autoRefresh) {
@@ -521,11 +600,21 @@ public class MoreSettingActivity extends BaseActivity {
@Override
public void finish() {
- Intent result = new Intent();
- result.putExtra(APPCONST.RESULT_NEED_REFRESH, needRefresh);
- result.putExtra(APPCONST.RESULT_UP_MENU, upMenu);
- setResult(AppCompatActivity.RESULT_OK, result);
- super.finish();
+ if (curFragment == null) {
+ Intent result = new Intent();
+ result.putExtra(APPCONST.RESULT_NEED_REFRESH, needRefresh);
+ result.putExtra(APPCONST.RESULT_UP_MENU, upMenu);
+ setResult(AppCompatActivity.RESULT_OK, result);
+ super.finish();
+ } else {
+ svContent.setVisibility(View.VISIBLE);
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ ft.hide(curFragment);
+ ft.commit();
+ curFragment = null;
+ setUpToolbar();
+ invalidateOptionsMenu();
+ }
}
private void initSpinner() {
@@ -638,7 +727,7 @@ public class MoreSettingActivity extends BaseActivity {
if (mBooks != null) {
return;
}
- mBooks = (ArrayList) BookService.getInstance().getAllBooks();
+ mBooks = (ArrayList) BookService.getInstance().getAllBooksNoHide();
Iterator mBooksIter = mBooks.iterator();
while (mBooksIter.hasNext()) {
@@ -656,5 +745,19 @@ public class MoreSettingActivity extends BaseActivity {
}
}
+ private void showPrivateBooksFragment(){
+ svContent.setVisibility(View.GONE);
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ if (mPrivateBooksFragment == null) {
+ mPrivateBooksFragment = new PrivateBooksFragment();
+ ft.add(R.id.ll_content, mPrivateBooksFragment);
+ } else {
+ ft.show(mPrivateBooksFragment);
+ mPrivateBooksFragment.init();
+ }
+ ft.commit();
+ curFragment = mPrivateBooksFragment;
+ setUpToolbar();
+ }
}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
index 6614b80..3868204 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
@@ -48,6 +48,7 @@ import xyz.fycz.myreader.enums.Font;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.BookMark;
import xyz.fycz.myreader.greendao.entity.Chapter;
+import xyz.fycz.myreader.greendao.service.BookGroupService;
import xyz.fycz.myreader.greendao.service.BookMarkService;
import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.greendao.service.ChapterService;
@@ -548,7 +549,9 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
intentFilter.addAction(Intent.ACTION_TIME_TICK);
registerReceiver(mReceiver, intentFilter);
//当书籍Collected且书籍id不为空的时候保存上次阅读信息
- if (isCollected && !StringHelper.isEmpty(mBook.getId())) {
+ String privateGroupId = SharedPreUtils.getInstance().getString("privateGroupId");
+ if (isCollected && !StringHelper.isEmpty(mBook.getId())
+ && !privateGroupId.equals(mBook.getGroupId())) {
//保存上次阅读信息
SharedPreUtils.getInstance().putString(getString(R.string.lastRead), mBook.getId());
}
@@ -923,7 +926,11 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
return;
}
- mBookService.addBook(book);
+ if (BookGroupService.getInstance().curGroupIsPrivate()){
+ mBookService.addBookNoGroup(book);
+ }else {
+ mBookService.addBook(book);
+ }
mBook = book;
}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java
index 571b9c7..7d82f43 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java
@@ -29,10 +29,13 @@ import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.common.APPCONST;
+import xyz.fycz.myreader.greendao.service.BookGroupService;
+import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.util.DateHelper;
import xyz.fycz.myreader.util.IOUtils;
import xyz.fycz.myreader.util.PermissionsChecker;
import xyz.fycz.myreader.util.SharedPreUtils;
+import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.ImageLoader;
import xyz.fycz.myreader.util.utils.MD5Utils;
@@ -59,6 +62,7 @@ public class SplashActivity extends BaseActivity {
try {
sleep(WAIT_INTERVAL);//使程序休眠
Intent it = new Intent(SplashActivity.this, MainActivity.class);//启动MainActivity
+ it.putExtra("startFromSplash", true);
startActivity(it);
finish();//关闭当前活动
} catch (Exception e) {
@@ -67,6 +71,7 @@ public class SplashActivity extends BaseActivity {
}
};
+
@Override
protected int getContentId() {
return R.layout.activity_splash;
@@ -89,6 +94,22 @@ public class SplashActivity extends BaseActivity {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M){
mPermissionsChecker = new PermissionsChecker(this);
requestPermission();
+ }else {
+ start();
+ }
+ }
+
+ private void start(){
+ if (BookGroupService.getInstance().curGroupIsPrivate()) {
+ MyApplication.runOnUiThread(() ->{
+ MyAlertDialog.showPrivateVerifyDia(SplashActivity.this, needGoTo -> {
+ myThread.start();
+ }, () -> {
+ SharedPreUtils.getInstance().putString(SplashActivity.this.getString(R.string.curBookGroupId), "");
+ SharedPreUtils.getInstance().putString(SplashActivity.this.getString(R.string.curBookGroupName), "");
+ myThread.start();
+ });
+ });
}else {
myThread.start();
}
@@ -166,7 +187,7 @@ public class SplashActivity extends BaseActivity {
if (mPermissionsChecker.lacksPermissions(PERMISSIONS)){
ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSIONS_REQUEST_STORAGE);
} else {
- myThread.start();
+ start();
}
}
@@ -179,7 +200,7 @@ public class SplashActivity extends BaseActivity {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//申请权限成功
- myThread.start();
+ start();
} else {
//申请权限失败
finish();
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/adapter/FileSystemAdapter.java b/app/src/main/java/xyz/fycz/myreader/ui/adapter/FileSystemAdapter.java
index 3b43bb8..26d38d9 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/adapter/FileSystemAdapter.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/adapter/FileSystemAdapter.java
@@ -94,8 +94,12 @@ public class FileSystemAdapter extends BaseListAdapter {
mCheckedCount = 0;
for (Map.Entry entry:entrys){
//必须是文件,必须没有被收藏
- if (entry.getKey().isFile() && !isFileLoaded(entry.getKey().getAbsolutePath())){
- entry.setValue(isChecked);
+ if (entry.getKey().isFile()){
+ if (!isFileLoaded(entry.getKey().getAbsolutePath())) {
+ entry.setValue(isChecked);
+ }else {
+ entry.setValue(false);
+ }
//如果选中,则增加点击的数量
if (isChecked){
++mCheckedCount;
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/dialog/BookGroupDialog.java b/app/src/main/java/xyz/fycz/myreader/ui/dialog/BookGroupDialog.java
new file mode 100644
index 0000000..c6549c7
--- /dev/null
+++ b/app/src/main/java/xyz/fycz/myreader/ui/dialog/BookGroupDialog.java
@@ -0,0 +1,233 @@
+package xyz.fycz.myreader.ui.dialog;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Handler;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.EditText;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.google.android.material.textfield.TextInputLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import xyz.fycz.myreader.R;
+import xyz.fycz.myreader.greendao.entity.Book;
+import xyz.fycz.myreader.greendao.entity.BookGroup;
+import xyz.fycz.myreader.greendao.service.BookGroupService;
+import xyz.fycz.myreader.greendao.service.BookService;
+import xyz.fycz.myreader.util.SharedPreUtils;
+import xyz.fycz.myreader.util.ToastUtils;
+
+/**
+ * @author fengyue
+ * @date 2021/1/8 23:58
+ */
+public class BookGroupDialog {
+ private ArrayList mBookGroups = new ArrayList<>();//书籍分组
+ private CharSequence[] mGroupNames;//书籍分组名称
+ private final BookGroupService mBookGroupService;
+ private Handler mHandler = new Handler();
+ private Context mContext;
+
+ public BookGroupDialog(Context context) {
+ this.mBookGroupService = BookGroupService.getInstance();
+ mContext = context;
+ }
+
+ public ArrayList getmBookGroups() {
+ return mBookGroups;
+ }
+
+ public CharSequence[] getmGroupNames() {
+ return mGroupNames;
+ }
+
+ //初始化书籍分组
+ public void initBookGroups(boolean isAdd) {
+ mBookGroups.clear();
+ mBookGroups.addAll(mBookGroupService.getAllGroups());
+ boolean openPrivate = SharedPreUtils.getInstance().getBoolean("openPrivate");
+ if (openPrivate) {
+ String privateGroupId = SharedPreUtils.getInstance().getString("privateGroupId");
+ mBookGroups.remove(BookGroupService.getInstance().getGroupById(privateGroupId));
+ }
+ mGroupNames = new CharSequence[isAdd ? mBookGroups.size() + 1 : mBookGroups.size()];
+ for (int i = 0; i < mBookGroups.size(); i++) {
+ BookGroup bookGroup = mBookGroups.get(i);
+ String groupName = bookGroup.getName();
+// mGroupNames[i] = groupName.getBytes().length > 20 ? groupName.substring(0, 8) + "···" : groupName;
+ mGroupNames[i] = groupName;
+ }
+ if (isAdd) {
+ mGroupNames[mBookGroups.size()] = "添加分组";
+ }
+ }
+
+
+ /**
+ * 加入分组
+ * @param book
+ */
+ public void addGroup(Book book, OnGroup onGroup){
+ List books = new ArrayList<>();
+ books.add(book);
+ addGroup(books, onGroup);
+ }
+
+ /**
+ * 加入批量分组
+ * @param mSelectBooks
+ * @param onGroup
+ */
+ public void addGroup(List mSelectBooks, OnGroup onGroup){
+ initBookGroups(true);
+ showSelectGroupDia((dialog, which) -> {
+ if (which < mBookGroups.size()) {
+ BookGroup bookGroup = mBookGroups.get(which);
+ for (Book book : mSelectBooks) {
+ if (!bookGroup.getId().equals(book.getGroupId())) {
+ book.setGroupId(bookGroup.getId());
+ book.setGroupSort(0);
+ }
+ }
+ BookService.getInstance().updateBooks(mSelectBooks);
+ ToastUtils.showSuccess("成功将《" + mSelectBooks.get(0).getName() + "》"
+ + (mSelectBooks.size() > 1 ? "等" : "")
+ + "加入[" + bookGroup.getName() + "]分组");
+ if (onGroup != null) onGroup.change();
+ } else if (which == mBookGroups.size()) {
+ showAddOrRenameGroupDia(false, true, 0, onGroup);
+ }
+ });
+ }
+ /**
+ * 添加/重命名分组对话框
+ */
+ public void showAddOrRenameGroupDia(boolean isRename, boolean isAddGroup, int groupNum, OnGroup onGroup){
+ View view = LayoutInflater.from(mContext).inflate(R.layout.edit_dialog, null);
+ TextInputLayout textInputLayout = view.findViewById(R.id.text_input_lay);
+ textInputLayout.setCounterMaxLength(10);
+ EditText editText = textInputLayout.getEditText();
+ editText.setHint("请输入分组名");
+ BookGroup bookGroup = !isRename ? new BookGroup() : mBookGroups.get(groupNum);
+ String oldName = bookGroup.getName();
+ if (isRename) {
+ editText.setText(oldName);
+ }
+ editText.requestFocus();
+ InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ mHandler.postDelayed(() ->{
+ imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
+ }, 220);
+ AlertDialog newGroupDia = MyAlertDialog.build(mContext)
+ .setTitle(!isRename ? "新建分组" : "重命名分组")
+ .setView(view)
+ .setCancelable(false)
+ .setPositiveButton("确认", null)
+ .setNegativeButton("取消", null)
+ .show();
+ Button posBtn = newGroupDia.getButton(AlertDialog.BUTTON_POSITIVE);
+ posBtn.setEnabled(false);
+ posBtn.setOnClickListener(v1 -> {
+ CharSequence newGroupName = editText.getText().toString();
+ for (CharSequence oldGroupName : mGroupNames){
+ if (oldGroupName.equals(newGroupName)){
+ ToastUtils.showWarring("分组[" + newGroupName + "]已存在,无法" + (!isRename ? "添加!" : "重命名!"));
+ return;
+ }
+ }
+ bookGroup.setName(newGroupName.toString());
+ if (!isRename) {
+ mBookGroupService.addBookGroup(bookGroup);
+ }else {
+ mBookGroupService.updateEntity(bookGroup);
+ SharedPreUtils spu = SharedPreUtils.getInstance();
+ if (spu.getString(mContext.getString(R.string.curBookGroupName), "").equals(oldName)){
+ spu.putString(mContext.getString(R.string.curBookGroupName), newGroupName.toString());
+ if (onGroup != null) onGroup.change();
+ }
+ }
+ ToastUtils.showSuccess("成功" +
+ (!isRename ? "添加分组[" : "成功将[" + oldName + "]重命名为[")
+ + bookGroup.getName() + "]");
+ imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
+ newGroupDia.dismiss();
+ if (isAddGroup){
+ if (onGroup != null) onGroup.addGroup();
+ }
+ });
+ editText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ String text = editText.getText().toString();
+ if (editText.getText().length() > 0 && editText.getText().length() <= 10 && !text.equals(oldName)) {
+ posBtn.setEnabled(true);
+ } else {
+ posBtn.setEnabled(false);
+ }
+ }
+ });
+ }
+
+ /**
+ * 删除分组对话框
+ */
+ public void showDeleteGroupDia(OnGroup onGroup) {
+ boolean[] checkedItems = new boolean[mGroupNames.length];
+ new MultiChoiceDialog().create(mContext, "删除分组", mGroupNames
+ , checkedItems, 0, (dialog, which) -> {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < checkedItems.length; i++) {
+ if (checkedItems[i]) {
+ mBookGroupService.deleteEntity(mBookGroups.get(i));
+ sb.append(mBookGroups.get(i).getName()).append("、");
+ }
+ }
+ if (sb.length() > 0){
+ sb.deleteCharAt(sb.lastIndexOf("、"));
+ }
+ SharedPreUtils spu = SharedPreUtils.getInstance();
+ if (mBookGroupService.getGroupById(spu.getString(mContext.getString(R.string.curBookGroupId), "")) == null){
+ spu.putString(mContext.getString(R.string.curBookGroupId), "");
+ spu.putString(mContext.getString(R.string.curBookGroupName), "");
+ onGroup.change();
+ }
+ ToastUtils.showSuccess("分组[" + sb.toString() + "]删除成功!");
+ }, null, null);
+ }
+ //显示选择书籍对话框
+ public void showSelectGroupDia(DialogInterface.OnClickListener onClickListener){
+ MyAlertDialog.build(mContext)
+ .setTitle("选择分组")
+ .setItems(mGroupNames, onClickListener)
+ .setCancelable(false)
+ .setPositiveButton("取消", null)
+ .show();
+ }
+
+ public abstract static class OnGroup{
+ public abstract void change();
+
+ public void addGroup() {
+
+ }
+ }
+}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/dialog/DialogCreator.java b/app/src/main/java/xyz/fycz/myreader/ui/dialog/DialogCreator.java
index e133e9c..37e3540 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/dialog/DialogCreator.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/dialog/DialogCreator.java
@@ -61,14 +61,11 @@ public class DialogCreator {
normalDialog.setNegativeButton("取消", negativeListener);
// 显示
final AlertDialog alertDialog = normalDialog.create();
- MyApplication.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- alertDialog.show();
- } catch (Exception e) {
- e.printStackTrace();
- }
+ MyApplication.runOnUiThread(() -> {
+ try {
+ alertDialog.show();
+ } catch (Exception e) {
+ e.printStackTrace();
}
});
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/dialog/FingerprintDialog.java b/app/src/main/java/xyz/fycz/myreader/ui/dialog/FingerprintDialog.java
new file mode 100644
index 0000000..fa28bd5
--- /dev/null
+++ b/app/src/main/java/xyz/fycz/myreader/ui/dialog/FingerprintDialog.java
@@ -0,0 +1,176 @@
+package xyz.fycz.myreader.ui.dialog;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.DialogFragment;
+
+import javax.crypto.Cipher;
+
+import xyz.fycz.myreader.R;
+import xyz.fycz.myreader.util.ToastUtils;
+
+/**
+ * @author fengyue
+ * @date 2021/1/9 14:50
+ */
+@TargetApi(23)
+public class FingerprintDialog extends DialogFragment {
+ private FingerprintManager fingerprintManager;
+
+ private CancellationSignal mCancellationSignal;
+
+ private Cipher mCipher;
+
+ private TextView errorMsg;
+
+ private boolean isUnlock;//是否解锁
+
+ private AppCompatActivity mActivity;
+
+ private OnAuthenticated onAuthenticated;
+
+ private OnCancelListener onCancelListener;
+
+ public FingerprintDialog(AppCompatActivity activity, boolean isUnlock, OnAuthenticated onAuthenticated) {
+ mActivity = activity;
+ this.isUnlock = isUnlock;
+ this.onAuthenticated = onAuthenticated;
+ }
+
+
+ /**
+ * 标识是否是用户主动取消的认证。
+ */
+ private boolean isSelfCancelled;
+
+ public void setCipher(Cipher cipher) {
+ mCipher = cipher;
+ }
+
+ public void setOnCancelListener(OnCancelListener onCancelListener) {
+ this.onCancelListener = onCancelListener;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ fingerprintManager = getContext().getSystemService(FingerprintManager.class);
+ setStyle(DialogFragment.STYLE_NORMAL, R.style.alertDialogTheme);
+ }
+
+ @Nullable
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fingerprint_dialog, container, false);
+ errorMsg = v.findViewById(R.id.error_msg);
+ TextView verifyFingerprint = v.findViewById(R.id.verify_fingerprint);
+ verifyFingerprint.setText(isUnlock ? R.string.verify_fingerprint : R.string.verify_has_fingerprint);
+ TextView cancel = v.findViewById(R.id.cancel);
+ TextView usePwd = v.findViewById(R.id.use_pwd);
+ if (isUnlock){
+ usePwd.setVisibility(View.VISIBLE);
+ usePwd.setOnClickListener(v1 ->{
+ dismiss();
+ stopListening();
+ MyAlertDialog.showPrivatePwdInputDia(mActivity, needGoTo -> {
+ onAuthenticated.onSuccess(needGoTo);
+ }, () ->{
+ if (onCancelListener != null) {
+ onCancelListener.cancel();
+ }
+ });
+ });
+ }else {
+ usePwd.setVisibility(View.GONE);
+ }
+ cancel.setOnClickListener(v1 -> {
+ dismiss();
+ stopListening();
+ if (onCancelListener != null) {
+ onCancelListener.cancel();
+ }
+ });
+ return v;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ // 开始指纹认证监听
+ startListening(mCipher);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ // 停止指纹认证监听
+ stopListening();
+ }
+
+ private void startListening(Cipher cipher) {
+ isSelfCancelled = false;
+ mCancellationSignal = new CancellationSignal();
+ fingerprintManager.authenticate(new FingerprintManager.CryptoObject(cipher), mCancellationSignal, 0, new FingerprintManager.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {
+ if (!isSelfCancelled) {
+ errorMsg.setText(errString);
+ if (errorCode == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT) {
+ ToastUtils.showError((String) errString);
+ dismiss();
+ if (onCancelListener != null) {
+ onCancelListener.cancel();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
+ errorMsg.setText(helpString);
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
+ ToastUtils.showSuccess("指纹认证成功");
+ onAuthenticated.onSuccess(true);
+ dismiss();
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ errorMsg.setText("指纹认证失败,请再试一次");
+ }
+ }, null);
+ }
+
+ private void stopListening() {
+ if (mCancellationSignal != null) {
+ mCancellationSignal.cancel();
+ mCancellationSignal = null;
+ isSelfCancelled = true;
+ }
+ }
+
+ public interface OnAuthenticated{
+ void onSuccess(boolean needGoTo);
+ }
+
+ public interface OnCancelListener{
+ void cancel();
+ }
+}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/dialog/MyAlertDialog.java b/app/src/main/java/xyz/fycz/myreader/ui/dialog/MyAlertDialog.java
index ba063ee..d4ce4ee 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/dialog/MyAlertDialog.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/dialog/MyAlertDialog.java
@@ -1,5 +1,6 @@
package xyz.fycz.myreader.ui.dialog;
+import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.text.Editable;
@@ -10,12 +11,21 @@ import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
+
import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.textfield.TextInputLayout;
+
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
+import xyz.fycz.myreader.common.APPCONST;
+import xyz.fycz.myreader.greendao.service.BookGroupService;
+import xyz.fycz.myreader.util.CyptoUtils;
+import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.StringHelper;
+import xyz.fycz.myreader.util.ToastUtils;
+import xyz.fycz.myreader.util.utils.FingerprintUtils;
/**
* @author fengyue
@@ -27,8 +37,16 @@ public class MyAlertDialog {
}
public static AlertDialog createInputDia(Context context, String title, String hint, String initText,
- Integer inputType, boolean cancelable, int maxLen, onInputChangeListener oic,
- DialogInterface.OnClickListener posListener) {
+ Integer inputType, boolean cancelable, int maxLen, onInputChangeListener oic,
+ DialogInterface.OnClickListener posListener) {
+ return createInputDia(context, title, hint, initText, inputType, cancelable, maxLen, oic, posListener, null, null, null);
+ }
+
+ public static AlertDialog createInputDia(Context context, String title, String hint, String initText,
+ Integer inputType, boolean cancelable, int maxLen, onInputChangeListener oic,
+ DialogInterface.OnClickListener posListener,
+ DialogInterface.OnClickListener negListener, String neutralBtn,
+ DialogInterface.OnClickListener neutralListener) {
View view = LayoutInflater.from(context).inflate(R.layout.edit_dialog, null);
TextInputLayout textInputLayout = view.findViewById(R.id.text_input_lay);
@@ -40,18 +58,42 @@ public class MyAlertDialog {
editText.requestFocus();
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
MyApplication.getHandler().postDelayed(() -> imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED), 220);
- AlertDialog inputDia = build(context)
- .setTitle(title)
- .setView(view)
- .setCancelable(cancelable)
- .setPositiveButton("确认", (dialog, which) -> {
- posListener.onClick(dialog, which);
- imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
- })
- .setNegativeButton("取消", (dialog, which) -> {
- imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
- })
- .show();
+ AlertDialog inputDia;
+ if (neutralBtn == null) {
+ inputDia = build(context)
+ .setTitle(title)
+ .setView(view)
+ .setCancelable(cancelable)
+ .setPositiveButton("确认", (dialog, which) -> {
+ posListener.onClick(dialog, which);
+ imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
+ })
+ .setNegativeButton("取消", (dialog, which) -> {
+ if (negListener != null) {
+ negListener.onClick(dialog, which);
+ }
+ imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
+ })
+ .show();
+ } else {
+ inputDia = build(context)
+ .setTitle(title)
+ .setView(view)
+ .setCancelable(cancelable)
+ .setNeutralButton(neutralBtn, (dialog, which) -> {
+ neutralListener.onClick(dialog, which);
+ imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
+ })
+ .setPositiveButton("确认", (dialog, which) -> {
+ posListener.onClick(dialog, which);
+ imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
+ })
+ .setNegativeButton("取消", (dialog, which) -> {
+ negListener.onClick(dialog, which);
+ imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
+ })
+ .show();
+ }
Button posBtn = inputDia.getButton(AlertDialog.BUTTON_POSITIVE);
posBtn.setEnabled(false);
editText.addTextChangedListener(new TextWatcher() {
@@ -78,6 +120,7 @@ public class MyAlertDialog {
});
return inputDia;
}
+
public static AlertDialog createInputDia(Context context, String title, String hint, String initText,
boolean cancelable, int maxLen, onInputChangeListener oic,
DialogInterface.OnClickListener posListener) {
@@ -85,7 +128,94 @@ public class MyAlertDialog {
}
- public interface onInputChangeListener{
+ /**
+ * 验证隐私密码对话框
+ *
+ * @param activity
+ * @param onVerify
+ */
+ public static void showPrivateVerifyDia(AppCompatActivity activity, OnVerify onVerify) {
+ showPrivateVerifyDia(activity, onVerify, null);
+ }
+ public static void showPrivateVerifyDia(AppCompatActivity activity, OnVerify onVerify, OnCancel onCancel) {
+ boolean openPrivate = SharedPreUtils.getInstance().getBoolean("openPrivate");
+ boolean openFingerprint = SharedPreUtils.getInstance().getBoolean("openFingerprint");
+ if (openPrivate) {
+ if (openFingerprint) {
+ FingerprintDialog fd = new FingerprintDialog(activity, true, onVerify::success);
+ fd.setCancelable(false);
+ fd.setCipher(FingerprintUtils.initCipher());
+ fd.setOnCancelListener(() -> {
+ if (onCancel != null) {
+ onCancel.cancel();
+ }
+ });
+ fd.show(activity.getSupportFragmentManager(), "fingerprint");
+ } else {
+ showPrivatePwdInputDia(activity, onVerify, onCancel);
+ }
+ } else {
+ onVerify.success(false);
+ }
+ }
+
+
+ /**
+ * 输入隐私密码对话框
+ * @param activity
+ * @param onVerify
+ */
+ public static void showPrivatePwdInputDia(AppCompatActivity activity, OnVerify onVerify){
+ showPrivatePwdInputDia(activity, onVerify, null);
+ }
+ public static void showPrivatePwdInputDia(AppCompatActivity activity, OnVerify onVerify, OnCancel onCancel){
+ final String[] pwd = new String[1];
+ String pwds = SharedPreUtils.getInstance().getString("privatePwd");
+ MyAlertDialog.createInputDia(activity, activity.getString(R.string.input_private_pwd),
+ "", "", InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD,
+ true, 12,
+ text -> pwd[0] = text,
+ (dialog, which) -> {
+ String pwde = CyptoUtils.encode(APPCONST.KEY, pwd[0]);
+ if (pwde.equals(pwds)) {
+ onVerify.success(true);
+ } else {
+ ToastUtils.showError("密码错误");
+ if (onCancel != null) {
+ onCancel.cancel();
+ }
+ }
+ }, (dialog, which) -> {
+ if (onCancel != null) {
+ onCancel.cancel();
+ }
+ },"忘记密码", (dialog, which) -> {
+ DialogCreator.createCommonDialog(activity, "忘记密码",
+ "忘记密码无法找回!\n您可点击确定按钮关闭私密书架并删除私密书架所有书籍,确定关闭吗?",
+ false, (dialog1, which1) -> {
+ BookGroupService.getInstance().deletePrivateGroup();
+ SharedPreUtils.getInstance().putBoolean("openPrivate", false);
+ SharedPreUtils.getInstance().putBoolean("openFingerprint", false);
+ SharedPreUtils.getInstance().putString("privatePwd", "");
+ ToastUtils.showSuccess("私密书架已关闭");
+ onVerify.success(false);
+ }, (dialog1, which1) -> {
+ if (onCancel != null) {
+ onCancel.cancel();
+ }
+ });
+ });
+ }
+
+ public interface OnVerify {
+ void success(boolean needGoTo);
+ }
+
+ public interface OnCancel {
+ void cancel();
+ }
+
+ public interface onInputChangeListener {
void onChange(String text);
}
}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/fragment/FileCategoryFragment.java b/app/src/main/java/xyz/fycz/myreader/ui/fragment/FileCategoryFragment.java
index 7cdb572..5d4aeec 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/fragment/FileCategoryFragment.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/fragment/FileCategoryFragment.java
@@ -86,21 +86,21 @@ public class FileCategoryFragment extends BaseFileFragment {
}
);
- mTvBackLast.setOnClickListener(
- (v) -> {
- FileStack.FileSnapshot snapshot = mFileStack.pop();
- int oldScrollOffset = mRvContent.computeHorizontalScrollOffset();
- if (snapshot == null) return;
- mTvPath.setText(snapshot.filePath);
- mAdapter.refreshItems(snapshot.files);
- mRvContent.scrollBy(0,snapshot.scrollOffset - oldScrollOffset);
- //反馈
- if (mListener != null){
- mListener.onCategoryChanged();
- }
- }
- );
+ mTvBackLast.setOnClickListener(v -> backLast());
+ }
+ public boolean backLast(){
+ FileStack.FileSnapshot snapshot = mFileStack.pop();
+ int oldScrollOffset = mRvContent.computeHorizontalScrollOffset();
+ if (snapshot == null) return false;
+ mTvPath.setText(snapshot.filePath);
+ mAdapter.refreshItems(snapshot.files);
+ mRvContent.scrollBy(0,snapshot.scrollOffset - oldScrollOffset);
+ //反馈
+ if (mListener != null){
+ mListener.onCategoryChanged();
+ }
+ return true;
}
@Override
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/fragment/PrivateBooksFragment.java b/app/src/main/java/xyz/fycz/myreader/ui/fragment/PrivateBooksFragment.java
new file mode 100644
index 0000000..c1cddcb
--- /dev/null
+++ b/app/src/main/java/xyz/fycz/myreader/ui/fragment/PrivateBooksFragment.java
@@ -0,0 +1,220 @@
+package xyz.fycz.myreader.ui.fragment;
+
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.InputType;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.SwitchCompat;
+
+import java.util.List;
+
+import butterknife.BindView;
+import xyz.fycz.myreader.R;
+import xyz.fycz.myreader.application.MyApplication;
+import xyz.fycz.myreader.base.BaseFragment;
+import xyz.fycz.myreader.common.APPCONST;
+import xyz.fycz.myreader.greendao.entity.Book;
+import xyz.fycz.myreader.greendao.service.BookGroupService;
+import xyz.fycz.myreader.greendao.service.BookService;
+import xyz.fycz.myreader.ui.dialog.DialogCreator;
+import xyz.fycz.myreader.ui.dialog.FingerprintDialog;
+import xyz.fycz.myreader.ui.dialog.MultiChoiceDialog;
+import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
+import xyz.fycz.myreader.util.CyptoUtils;
+import xyz.fycz.myreader.util.SharedPreUtils;
+import xyz.fycz.myreader.util.ToastUtils;
+import xyz.fycz.myreader.util.utils.FingerprintUtils;
+
+/**
+ * @author fengyue
+ * @date 2021/1/9 13:53
+ */
+public class PrivateBooksFragment extends BaseFragment {
+ @BindView(R.id.rl_private_bookcase)
+ RelativeLayout mRlPrivateBookcase;
+ @BindView(R.id.sc_private_bookcase)
+ SwitchCompat mScPrivateBookcase;
+ @BindView(R.id.ll_content)
+ LinearLayout mLlContent;
+ @BindView(R.id.ll_hide_books)
+ LinearLayout mLlHideBooks;
+ @BindView(R.id.rl_change_pwd)
+ RelativeLayout mRlChangePwd;
+ @BindView(R.id.rl_fingerprint)
+ RelativeLayout mRlFingerprint;
+ @BindView(R.id.sc_fingerprint)
+ SwitchCompat mScFingerprint;
+
+ private boolean openPrivate;
+ private boolean openFingerprint;
+
+ @Override
+ protected int getContentId() {
+ return R.layout.fragment_private_bookcase;
+ }
+
+ @Override
+ protected void initData(Bundle savedInstanceState) {
+ super.initData(savedInstanceState);
+ openPrivate = SharedPreUtils.getInstance().getBoolean("openPrivate");
+ openFingerprint = SharedPreUtils.getInstance().getBoolean("openFingerprint");
+ }
+
+ @Override
+ protected void initWidget(Bundle savedInstanceState) {
+ super.initWidget(savedInstanceState);
+ mScPrivateBookcase.setChecked(openPrivate);
+ mScFingerprint.setChecked(openFingerprint);
+ if (openPrivate) mLlContent.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ protected void initClick() {
+ super.initClick();
+ mRlPrivateBookcase.setOnClickListener(v -> {
+ if (openPrivate) {
+ DialogCreator.createCommonDialog(getContext(), "关闭私密书架",
+ "确定要关闭私密书架吗?\n注意:这将会删除私密书架中的全部书籍!",
+ true, (dialog, which) -> {
+ BookGroupService.getInstance().deletePrivateGroup();
+ SharedPreUtils.getInstance().putString("privatePwd", "");
+ mLlContent.setVisibility(View.GONE);
+ openPrivate = !openPrivate;
+ openFingerprint = false;
+ SharedPreUtils.getInstance().putBoolean("openPrivate", openPrivate);
+ SharedPreUtils.getInstance().putBoolean("openFingerprint", openFingerprint);
+ mScPrivateBookcase.setChecked(openPrivate);
+ mScFingerprint.setChecked(openFingerprint);
+ }, null);
+ } else {
+ final String[] pwd = new String[1];
+ MyAlertDialog.createInputDia(getContext(), getString(R.string.set_private_pwd),
+ "", "", InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD,
+ true, 12,
+ text -> pwd[0] = text,
+ (dialog, which) -> {
+ BookGroupService.getInstance().createPrivateGroup();
+ SharedPreUtils.getInstance().putString("privatePwd", CyptoUtils.encode(APPCONST.KEY, pwd[0]));
+ dialog.dismiss();
+ mLlContent.setVisibility(View.VISIBLE);
+ openPrivate = !openPrivate;
+ SharedPreUtils.getInstance().putBoolean("openPrivate", openPrivate);
+ mScPrivateBookcase.setChecked(openPrivate);
+ });
+ }
+ });
+
+ mLlHideBooks.setOnClickListener(v -> {
+ MyApplication.runOnUiThread(() -> {
+ String privateGroupId = SharedPreUtils.getInstance().getString("privateGroupId");
+ List mBooks = BookService.getInstance().getAllBooks();
+
+ int booksCount = mBooks.size();
+
+ if (booksCount == 0) {
+ ToastUtils.showWarring("当前书架没有任何书籍!");
+ return;
+ }
+
+ CharSequence[] mBooksName = new CharSequence[booksCount];
+
+ for (int i = 0; i < booksCount; i++) {
+ Book book = mBooks.get(i);
+ mBooksName[i] = !"本地书籍".equals(book.getType()) ? book.getName() : book.getName() + "[本地]";
+ }
+
+ boolean[] isPrivate = new boolean[booksCount];
+ int crBookCount = 0;
+
+ for (int i = 0; i < booksCount; i++) {
+ Book book = mBooks.get(i);
+ isPrivate[i] = privateGroupId.equals(book.getGroupId());
+ if (isPrivate[i]) {
+ crBookCount++;
+ }
+ }
+
+ new MultiChoiceDialog().create(getContext(), "隐藏的书籍",
+ mBooksName, isPrivate, crBookCount, (dialog, which) -> {
+ BookService.getInstance().updateBooks(mBooks);
+ }, null, new DialogCreator.OnMultiDialogListener() {
+ @Override
+ public void onItemClick(DialogInterface dialog, int which, boolean isChecked) {
+ if (isChecked) {
+ mBooks.get(which).setGroupId(privateGroupId);
+ }else {
+ mBooks.get(which).setGroupId("");
+ }
+ }
+
+ @Override
+ public void onSelectAll(boolean isSelectAll) {
+ for (Book book : mBooks) {
+ book.setGroupId(privateGroupId);
+ }
+ }
+ }).show();
+
+ });
+ });
+
+ mRlChangePwd.setOnClickListener(v -> {
+ final String[] pwd = new String[1];
+ MyAlertDialog.createInputDia(getContext(), getString(R.string.change_pwd),
+ "", "", InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD,
+ true, 12,
+ text -> pwd[0] = text,
+ (dialog, which) -> {
+ SharedPreUtils.getInstance().putString("privatePwd", CyptoUtils.encode(APPCONST.KEY, pwd[0]));
+ dialog.dismiss();
+ mLlContent.setVisibility(View.VISIBLE);
+ });
+ });
+
+ mRlFingerprint.setOnClickListener(
+ (v) -> {
+ if (openFingerprint) {
+ openFingerprint = false;
+ mScFingerprint.setChecked(openFingerprint);
+ SharedPreUtils.getInstance().putBoolean("openFingerprint", openFingerprint);
+ } else {
+ if (!FingerprintUtils.supportFingerprint(getActivity())) return;
+ FingerprintDialog fd = new FingerprintDialog((AppCompatActivity) getActivity(),false, needGoTo -> {
+ openFingerprint = true;
+ mScFingerprint.setChecked(openFingerprint);
+ SharedPreUtils.getInstance().putBoolean("openFingerprint", openFingerprint);
+ });
+ fd.setCancelable(false);
+ fd.setCipher(FingerprintUtils.initCipher());
+ fd.show(getFragmentManager(), "fingerprint");
+ }
+ }
+ );
+ }
+
+ @Override
+ protected void processLogic() {
+ super.processLogic();
+ if (!SharedPreUtils.getInstance().getBoolean("isReadPrivateTip")){
+ DialogCreator.createTipDialog(getContext(), "关于私密书架", getString(R.string.private_bookcase_tip));
+ SharedPreUtils.getInstance().putBoolean("isReadPrivateTip", true);
+ }
+ }
+
+ public void init(){
+ openPrivate = SharedPreUtils.getInstance().getBoolean("openPrivate");
+ openFingerprint = SharedPreUtils.getInstance().getBoolean("openFingerprint");
+ mScPrivateBookcase.setChecked(openPrivate);
+ mScFingerprint.setChecked(openFingerprint);
+ if (openPrivate) {
+ mLlContent.setVisibility(View.VISIBLE);
+ }else {
+ mLlContent.setVisibility(View.GONE);
+ }
+ }
+
+}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/WebDavSettingActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/fragment/WebDavFragment.java
similarity index 68%
rename from app/src/main/java/xyz/fycz/myreader/ui/activity/WebDavSettingActivity.java
rename to app/src/main/java/xyz/fycz/myreader/ui/fragment/WebDavFragment.java
index ec0e659..0d88f44 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/activity/WebDavSettingActivity.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/fragment/WebDavFragment.java
@@ -1,37 +1,36 @@
-package xyz.fycz.myreader.ui.activity;
+package xyz.fycz.myreader.ui.fragment;
import android.os.Bundle;
import android.text.InputType;
-import android.view.Menu;
-import android.view.MenuItem;
+import android.view.LayoutInflater;
+import android.view.View;
import android.widget.LinearLayout;
+import android.widget.NumberPicker;
import android.widget.TextView;
-import androidx.annotation.NonNull;
-import androidx.appcompat.widget.Toolbar;
+
+import java.util.ArrayList;
+
import butterknife.BindView;
import io.reactivex.Single;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import xyz.fycz.myreader.R;
-import xyz.fycz.myreader.base.BaseActivity;
+import xyz.fycz.myreader.base.BaseFragment;
import xyz.fycz.myreader.base.observer.MySingleObserver;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.model.storage.BackupRestoreUi;
import xyz.fycz.myreader.model.storage.WebDavHelp;
-import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.ToastUtils;
-import java.util.ArrayList;
-
/**
* @author fengyue
- * @date 2020/10/4 20:44
+ * @date 2021/1/9 12:08
*/
-public class WebDavSettingActivity extends BaseActivity {
+public class WebDavFragment extends BaseFragment {
@BindView(R.id.webdav_setting_webdav_url)
LinearLayout llWebdavUrl;
@BindView(R.id.tv_webdav_url)
@@ -46,20 +45,18 @@ public class WebDavSettingActivity extends BaseActivity {
TextView tvWebdavPassword;
@BindView(R.id.webdav_setting_webdav_restore)
LinearLayout llWebdavRestore;
+ @BindView(R.id.ll_restore_num)
+ LinearLayout mLlRestoreNum;
+ @BindView(R.id.tv_restore_num)
+ TextView mTvRestoreNum;
private String webdavUrl;
private String webdavAccount;
private String webdavPassword;
+ private int restoreNum;
@Override
protected int getContentId() {
- return R.layout.activity_webdav_setting;
- }
-
- @Override
- protected void setUpToolbar(Toolbar toolbar) {
- super.setUpToolbar(toolbar);
- setStatusBarColor(R.color.colorPrimary, true);
- getSupportActionBar().setTitle(getString(R.string.webdav_setting));
+ return R.layout.fragment_webdav_setting;
}
@Override
@@ -68,14 +65,16 @@ public class WebDavSettingActivity extends BaseActivity {
webdavUrl = SharedPreUtils.getInstance().getString("webdavUrl", APPCONST.DEFAULT_WEB_DAV_URL);
webdavAccount = SharedPreUtils.getInstance().getString("webdavAccount", "");
webdavPassword = SharedPreUtils.getInstance().getString("webdavPassword", "");
+ restoreNum = SharedPreUtils.getInstance().getInt("restoreNum", 30);
}
@Override
- protected void initWidget() {
- super.initWidget();
+ protected void initWidget(Bundle savedInstanceState) {
+ super.initWidget(savedInstanceState);
tvWebdavUrl.setText(webdavUrl);
tvWebdavAccount.setText(StringHelper.isEmpty(webdavAccount) ? "请输入WebDav账号" : webdavAccount);
tvWebdavPassword.setText(StringHelper.isEmpty(webdavPassword) ? "请输入WebDav授权密码" : "************");
+ mTvRestoreNum.setText(getString(R.string.cur_restore_list_num, restoreNum));
}
@Override
@@ -83,9 +82,9 @@ public class WebDavSettingActivity extends BaseActivity {
super.initClick();
final String[] webdavTexts = new String[3];
llWebdavUrl.setOnClickListener(v -> {
- MyAlertDialog.createInputDia(this, getString(R.string.webdav_url),
+ MyAlertDialog.createInputDia(getContext(), getString(R.string.webdav_url),
"", webdavUrl.equals(APPCONST.DEFAULT_WEB_DAV_URL) ?
- "" : webdavUrl, true, 100,
+ "" : webdavUrl, true, 100,
text -> webdavTexts[0] = text,
(dialog, which) -> {
webdavUrl = webdavTexts[0];
@@ -95,7 +94,7 @@ public class WebDavSettingActivity extends BaseActivity {
});
});
llWebdavAccount.setOnClickListener(v -> {
- MyAlertDialog.createInputDia(this, getString(R.string.webdav_account),
+ MyAlertDialog.createInputDia(getContext(), getString(R.string.webdav_account),
"", webdavAccount, true, 100,
text -> webdavTexts[1] = text,
(dialog, which) -> {
@@ -106,7 +105,7 @@ public class WebDavSettingActivity extends BaseActivity {
});
});
llWebdavPassword.setOnClickListener(v -> {
- MyAlertDialog.createInputDia(this, getString(R.string.webdav_password),
+ MyAlertDialog.createInputDia(getContext(), getString(R.string.webdav_password),
"", webdavPassword, InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD,
true, 100,
text -> webdavTexts[2] = text,
@@ -125,27 +124,31 @@ public class WebDavSettingActivity extends BaseActivity {
.subscribe(new MySingleObserver>() {
@Override
public void onSuccess(ArrayList strings) {
- if (!WebDavHelp.INSTANCE.showRestoreDialog(WebDavSettingActivity.this, strings, BackupRestoreUi.INSTANCE)) {
+ if (!WebDavHelp.INSTANCE.showRestoreDialog(getContext(), strings, BackupRestoreUi.INSTANCE)) {
ToastUtils.showWarring("没有备份");
}
}
});
});
- }
-
+ mLlRestoreNum.setOnClickListener(v -> {
+ View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_number_picker, null);
+ NumberPicker threadPick = view.findViewById(R.id.number_picker);
+ threadPick.setMaxValue(100);
+ threadPick.setMinValue(10);
+ threadPick.setValue(restoreNum);
+ threadPick.setOnScrollListener((view1, scrollState) -> {
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.webdav_help, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(@NonNull MenuItem item) {
- if (item.getItemId() == R.id.action_tip){
- DialogCreator.createAssetTipDialog(this, "如何使用WebDav进行云备份?", "webdavhelp.fy");
- }
- return super.onOptionsItemSelected(item);
+ });
+ MyAlertDialog.build(getContext())
+ .setTitle("最大显示数")
+ .setView(view)
+ .setPositiveButton("确定", (dialog, which) -> {
+ restoreNum = threadPick.getValue();
+ SharedPreUtils.getInstance().putInt("restoreNum", restoreNum);
+ mTvRestoreNum.setText(getString(R.string.cur_restore_list_num, restoreNum));
+ }).setNegativeButton("取消", null)
+ .show();
+ });
}
}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java
index e7ffe30..bcfd7ef 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java
@@ -38,6 +38,7 @@ import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BasePresenter;
+import xyz.fycz.myreader.ui.dialog.BookGroupDialog;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.ui.dialog.MultiChoiceDialog;
@@ -108,6 +109,7 @@ public class BookcasePresenter implements BasePresenter {
private boolean isFirstRefresh = true;//是否首次进入刷新
private boolean isGroup;
private MainActivity.OnGroupChangeListener ogcl;
+ private final BookGroupDialog mBookGroupDia;
public static final String CANCEL_ACTION = "cancelAction";
@@ -180,6 +182,7 @@ public class BookcasePresenter implements BasePresenter {
mMainActivity = (MainActivity) (mBookcaseFragment.getActivity());
// mChapterService = new ChapterService();
mSetting = SysManager.getSetting();
+ mBookGroupDia = new BookGroupDialog(mMainActivity);
}
//启动
@@ -265,7 +268,20 @@ public class BookcasePresenter implements BasePresenter {
//加入分组监听器
mBookcaseFragment.getmBtnAddGroup().setOnClickListener(v -> {
- initBookGroups(true);
+ mBookGroupDia.addGroup(mBookcaseAdapter.getSelectBooks(), new BookGroupDialog.OnGroup(){
+ @Override
+ public void change() {
+ init();
+ if (hasOnGroupChangeListener())
+ ogcl.onChange();
+ }
+
+ @Override
+ public void addGroup() {
+ mBookcaseFragment.getmBtnAddGroup().performClick();
+ }
+ });
+ /*initBookGroups(true);
showSelectGroupDia((dialog, which) -> {
if (which < mBookGroups.size()) {
BookGroup bookGroup = mBookGroups.get(which);
@@ -284,7 +300,7 @@ public class BookcasePresenter implements BasePresenter {
} else if (which == mBookGroups.size()) {
showAddOrRenameGroupDia(false, true, 0);
}
- });
+ });*/
});
}
@@ -336,6 +352,14 @@ public class BookcasePresenter implements BasePresenter {
private void initBook() {
mBooks.clear();
String curBookGroupId = SharedPreUtils.getInstance().getString(mMainActivity.getString(R.string.curBookGroupId), "");
+ BookGroup bookGroup = mBookGroupService.getGroupById(curBookGroupId);
+ if (bookGroup == null) {
+ curBookGroupId = "";
+ SharedPreUtils.getInstance().putString(mMainActivity.getString(R.string.curBookGroupId), "");
+ SharedPreUtils.getInstance().putString(mMainActivity.getString(R.string.curBookGroupName), "");
+ if (hasOnGroupChangeListener())
+ ogcl.onChange();
+ }
isGroup = !"".equals(curBookGroupId);
if (mBookcaseAdapter != null) {
mBookcaseAdapter.setGroup(isGroup);
@@ -485,7 +509,6 @@ public class BookcasePresenter implements BasePresenter {
if (mPermissionsChecker == null) {
mPermissionsChecker = new PermissionsChecker(mMainActivity);
}
-
//获取读取和写入SD卡的权限
if (mPermissionsChecker.lacksPermissions(PERMISSIONS)) {
//请求权限
@@ -518,18 +541,18 @@ public class BookcasePresenter implements BasePresenter {
*
*/
public void showBookGroupMenu(View view) {
- initBookGroups(false);
+ mBookGroupDia.initBookGroups(false);
PopupMenu popupMenu = new PopupMenu(mMainActivity, view, Gravity.END);
popupMenu.getMenu().add(0, 0, 0, "所有书籍");
- for (int i = 0; i < mGroupNames.length; i++) {
- popupMenu.getMenu().add(0, 0, i + 1, mGroupNames[i]);
+ for (int i = 0; i < mBookGroupDia.getmGroupNames().length; i++) {
+ popupMenu.getMenu().add(0, 0, i + 1, mBookGroupDia.getmGroupNames()[i]);
}
popupMenu.setOnMenuItemClickListener(menuItem -> {
String curBookGroupId = "";
String curBookGroupName = "";
if (menuItem.getOrder() > 0) {
- curBookGroupId = mBookGroups.get(menuItem.getOrder() - 1).getId();
- curBookGroupName = mBookGroups.get(menuItem.getOrder() - 1).getName();
+ curBookGroupId = mBookGroupDia.getmBookGroups().get(menuItem.getOrder() - 1).getId();
+ curBookGroupName = mBookGroupDia.getmBookGroups().get(menuItem.getOrder() - 1).getName();
}
SharedPreUtils.getInstance().putString(mMainActivity.getString(R.string.curBookGroupId), curBookGroupId);
SharedPreUtils.getInstance().putString(mMainActivity.getString(R.string.curBookGroupName), curBookGroupName);
@@ -580,18 +603,44 @@ public class BookcasePresenter implements BasePresenter {
.setTitle("分组管理")
.setItems(mMainActivity.getResources().getStringArray(R.array.group_man)
, (dialog, which) -> {
- initBookGroups(false);
+ mBookGroupDia.initBookGroups(false);
switch (which){
case 0:
- showAddOrRenameGroupDia(false, false,0);
+ mBookGroupDia.showAddOrRenameGroupDia(false, false, 0, new BookGroupDialog.OnGroup() {
+ @Override
+ public void change() {
+ ogcl.onChange();
+ }
+
+ @Override
+ public void addGroup() {
+ mBookcaseFragment.getmBtnAddGroup().performClick();
+ }
+ });
break;
case 1:
- showSelectGroupDia((dialog1, which1) -> {
- showAddOrRenameGroupDia(true,false, which1);
+ mBookGroupDia.showSelectGroupDia((dialog1, which1) -> {
+ mBookGroupDia.showAddOrRenameGroupDia(true,false, which1, new BookGroupDialog.OnGroup() {
+ @Override
+ public void change() {
+ ogcl.onChange();
+ }
+
+ @Override
+ public void addGroup() {
+ mBookcaseFragment.getmBtnAddGroup().performClick();
+ }
+ });
});
break;
case 2:
- showDeleteGroupDia();
+ mBookGroupDia.showDeleteGroupDia(new BookGroupDialog.OnGroup() {
+ @Override
+ public void change() {
+ ogcl.onChange();
+ init();
+ }
+ });
break;
}
}).show();
diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/FingerprintUtils.java b/app/src/main/java/xyz/fycz/myreader/util/utils/FingerprintUtils.java
new file mode 100644
index 0000000..8498879
--- /dev/null
+++ b/app/src/main/java/xyz/fycz/myreader/util/utils/FingerprintUtils.java
@@ -0,0 +1,80 @@
+package xyz.fycz.myreader.util.utils;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Build;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
+import java.security.KeyStore;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+import xyz.fycz.myreader.util.ToastUtils;
+
+/**
+ * @author fengyue
+ * @date 2021/1/9 15:13
+ */
+public class FingerprintUtils {
+ private static final String DEFAULT_KEY_NAME = "fyreader";
+
+ static KeyStore keyStore;
+
+ public static boolean supportFingerprint(Activity activity) {
+ if (Build.VERSION.SDK_INT < 23) {
+ ToastUtils.showWarring("您的系统版本过低,不支持指纹功能");
+ return false;
+ } else {
+ KeyguardManager keyguardManager = activity.getSystemService(KeyguardManager.class);
+ FingerprintManager fingerprintManager = activity.getSystemService(FingerprintManager.class);
+ if (!fingerprintManager.isHardwareDetected()) {
+ ToastUtils.showWarring("您的手机不支持指纹功能");
+ return false;
+ } else if (!fingerprintManager.hasEnrolledFingerprints()) {
+ ToastUtils.showWarring("您至少需要在系统设置中添加一个指纹");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @TargetApi(23)
+ private static void initKey() {
+ try {
+ keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
+ KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(DEFAULT_KEY_NAME,
+ KeyProperties.PURPOSE_ENCRYPT |
+ KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+ .setUserAuthenticationRequired(true)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
+ keyGenerator.init(builder.build());
+ keyGenerator.generateKey();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @TargetApi(23)
+ public static Cipher initCipher() {
+ try {
+ initKey();
+ SecretKey key = (SecretKey) keyStore.getKey(DEFAULT_KEY_NAME, null);
+ Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ + KeyProperties.BLOCK_MODE_CBC + "/"
+ + KeyProperties.ENCRYPTION_PADDING_PKCS7);
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+ return cipher;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
diff --git a/app/src/main/res/drawable-xhdpi/ic_fp_40px.png b/app/src/main/res/drawable-xhdpi/ic_fp_40px.png
new file mode 100644
index 0000000000000000000000000000000000000000..f7e87240e1a3b35f0fb4b71d908ca9db7ac705bf
GIT binary patch
literal 18565
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
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
-
+
-
+
+
+
-
-
-
+
+
-
+
+
-
+ android:text="@string/webdav_setting_tip"
+ android:textColor="@color/textAssist" />
+
-
-
+ android:text="@string/read_setting"
+ android:textColor="@color/textPrimary" />
+
+
-
-
+ android:paddingRight="20dp">
-
+ android:textSize="@dimen/text_normal_size" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+ android:text="@string/bookcase_setting"
+ android:textColor="@color/textPrimary" />
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+ android:paddingRight="20dp">
-
-
+
+
-
+
+
-
+ android:text="@string/auto_refresh_tip"
+ android:textColor="@color/textAssist" />
+
-
-
+ android:longClickable="false" />
+
-
-
+
+
-
+
+
-
-
+
+
+
-
-
-
+
+
+
-
+
+
-
+
+
-
-
+
+
+
-
+
+
-
+
+
-
-
+
+
+
-
-
+
+
+ app:tint="@color/textAssist" />
-
+
-
+ android:paddingRight="20dp">
-
+ android:textSize="@dimen/text_normal_size" />
-
-
+ android:longClickable="false" />
+
-
+ android:paddingRight="20dp">
-
+ android:textSize="@dimen/text_normal_size" />
-
-
+ android:dropDownWidth="60dp"
+ android:gravity="center"
+ android:longClickable="false"
+ android:textColor="@color/textSecondary" />
+
-
-
+ android:text="@string/cathe_setting"
+ android:textColor="@color/textPrimary" />
-
+
-
+ android:paddingRight="20dp">
-
+ android:textSize="@dimen/text_normal_size" />
-
-
+ android:dropDownWidth="75dp"
+ android:gravity="center"
+ android:longClickable="false"
+ android:textColor="@color/textSecondary" />
+
-
-
+
+
-
+
+
-
-
+
+
+
+ android:paddingRight="20dp">
-
+ android:textSize="@dimen/text_normal_size" />
-
+
-
+ android:orientation="vertical"
+ android:layout_centerVertical="true"
+ android:background="@drawable/selector_common_bg">
+
+
+
+
+
+ -->
-
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fingerprint_dialog.xml b/app/src/main/res/layout/fingerprint_dialog.xml
new file mode 100644
index 0000000..51d14be
--- /dev/null
+++ b/app/src/main/res/layout/fingerprint_dialog.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_private_bookcase.xml b/app/src/main/res/layout/fragment_private_bookcase.xml
new file mode 100644
index 0000000..530d196
--- /dev/null
+++ b/app/src/main/res/layout/fragment_private_bookcase.xml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_webdav_setting.xml b/app/src/main/res/layout/fragment_webdav_setting.xml
similarity index 77%
rename from app/src/main/res/layout/activity_webdav_setting.xml
rename to app/src/main/res/layout/fragment_webdav_setting.xml
index 455b969..2858de4 100644
--- a/app/src/main/res/layout/activity_webdav_setting.xml
+++ b/app/src/main/res/layout/fragment_webdav_setting.xml
@@ -6,7 +6,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
-
@@ -44,14 +43,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/text_normal_size"
- android:textColor="@color/textPrimary"
+ android:textColor="@color/textSecondary"
android:text="@string/webdav_account"/>
@@ -68,14 +67,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/text_normal_size"
- android:textColor="@color/textPrimary"
+ android:textColor="@color/textSecondary"
android:text="@string/webdav_password"/>
@@ -92,14 +91,40 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/text_normal_size"
- android:textColor="@color/textPrimary"
+ android:textColor="@color/textSecondary"
android:text="@string/menu_backup_restore"/>
+
+
+
+
+
+
+
\ 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
index e3ced2e..d5ebc47 100644
--- a/app/src/main/res/menu/menu_book_detail.xml
+++ b/app/src/main/res/menu/menu_book_detail.xml
@@ -38,6 +38,5 @@
android:id="@+id/action_group_setting"
android:title="@string/menu_group_setting"
android:icon="@drawable/ic_group"
- app:showAsAction="never"
- android:visible="false"/>
+ app:showAsAction="never" />
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4464013..38fabc8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -65,7 +65,7 @@
编辑书架
切换布局
- 添加本地
+ 本地导入
同步书架
一键缓存
应用设置
@@ -280,6 +280,22 @@
导入
导出
重置布局
+ 恢复列表最大显示数
+ 当前显示数:%1$d
+ 私密书架
+ 隐藏书籍
+ 选择隐藏的书籍
+ 更改密码
+ 请输入隐私密码
+ 使用指纹解锁
+ 隐私密码
+ 设置隐私密码
+ 请验证指纹
+ 请验证已有指纹
+ 1、什么是私密书架?\n私密书架是设置隐私密码后自动创建的书籍分组,
+ 此书架的书籍仅能在该分组内阅读、查看及其他操作,且阅读后不会保存上次阅读记录。\n\n2、如何进入私密书架?\n在书架页面长按左上角书架标题正确验证隐私密码即可进入私密书架。
+ \n\n3、如何退出私密书架?\n点击书架菜单栏切换分组按钮切换至其他分组即可退出私密书架。\n\n4、忘记隐私密码怎么办?\n隐私密码不支持找回,忘记密码后可强制关闭私密书架,并删除私密书架内所有书籍。\n\n
+ 5、此功能处于测试阶段,如发现问题,请到我的-建议反馈里进行反馈。
- 常亮
diff --git a/app/version_code.properties b/app/version_code.properties
index 74f27ed..e10469d 100644
--- a/app/version_code.properties
+++ b/app/version_code.properties
@@ -1,2 +1,2 @@
-#Fri Jan 08 23:11:19 CST 2021
-VERSION_CODE=177
+#Sat Jan 09 20:56:54 CST 2021
+VERSION_CODE=178