新增私密书架

pull/5/head
fengyuecanzhu 5 years ago
parent d0e5b5cd72
commit 9ffec429a7
  1. BIN
      .idea/caches/build_file_checksums.ser
  2. 1
      app/build.gradle
  3. 3
      app/src/main/AndroidManifest.xml
  4. 13
      app/src/main/assets/updatelog.fy
  5. 2
      app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java
  6. 30
      app/src/main/java/xyz/fycz/myreader/greendao/service/BookGroupService.java
  7. 37
      app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java
  8. 6
      app/src/main/java/xyz/fycz/myreader/model/storage/WebDavHelp.kt
  9. 14
      app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
  10. 11
      app/src/main/java/xyz/fycz/myreader/ui/activity/FileSystemActivity.java
  11. 62
      app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java
  12. 131
      app/src/main/java/xyz/fycz/myreader/ui/activity/MoreSettingActivity.java
  13. 11
      app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
  14. 25
      app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java
  15. 8
      app/src/main/java/xyz/fycz/myreader/ui/adapter/FileSystemAdapter.java
  16. 233
      app/src/main/java/xyz/fycz/myreader/ui/dialog/BookGroupDialog.java
  17. 13
      app/src/main/java/xyz/fycz/myreader/ui/dialog/DialogCreator.java
  18. 176
      app/src/main/java/xyz/fycz/myreader/ui/dialog/FingerprintDialog.java
  19. 160
      app/src/main/java/xyz/fycz/myreader/ui/dialog/MyAlertDialog.java
  20. 28
      app/src/main/java/xyz/fycz/myreader/ui/fragment/FileCategoryFragment.java
  21. 220
      app/src/main/java/xyz/fycz/myreader/ui/fragment/PrivateBooksFragment.java
  22. 83
      app/src/main/java/xyz/fycz/myreader/ui/fragment/WebDavFragment.java
  23. 75
      app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java
  24. 80
      app/src/main/java/xyz/fycz/myreader/util/utils/FingerprintUtils.java
  25. BIN
      app/src/main/res/drawable-xhdpi/ic_fp_40px.png
  26. 786
      app/src/main/res/layout/activity_more_setting.xml
  27. 77
      app/src/main/res/layout/fingerprint_dialog.xml
  28. 114
      app/src/main/res/layout/fragment_private_bookcase.xml
  29. 43
      app/src/main/res/layout/fragment_webdav_setting.xml
  30. 3
      app/src/main/res/menu/menu_book_detail.xml
  31. 18
      app/src/main/res/values/strings.xml
  32. 4
      app/version_code.properties

@ -99,6 +99,7 @@ android {
}
productFlavors {
}
}

@ -5,6 +5,8 @@
<!-- ********************************permission************************************ -->
<!-- 读取手机状态和身份 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="com.example.broadcast.permission"/>
<!-- 拥有完全的网络访问权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
@ -102,7 +104,6 @@
<activity android:name=".ui.activity.MoreSettingActivity"/>
<activity android:name=".ui.activity.BookstoreActivity"/>
<activity android:name=".ui.activity.WebDavSettingActivity"/>
<activity android:name=".ui.activity.QRCodeScanActivity"/>
<receiver android:name=".util.notification.NotificationClickReceiver"/>

@ -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

@ -38,7 +38,7 @@ public abstract class BaseActivity extends AppCompatActivity {
protected CompositeDisposable mDisposable;
//ButterKnife
private Toolbar mToolbar;
protected Toolbar mToolbar;
private Unbinder unbinder;

@ -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()

@ -77,6 +77,21 @@ public class BookService extends BaseService {
.list();
}
/**
* 获取所有的书
*
* @return
*/
public List<Book> getAllBooksNoHide() {
List<Book> oldBooks = getAllBooks();
List<Book> 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<Book> 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);
}
}
/**
* 查询书籍总数
*

@ -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)
}
}

@ -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<Chapter>) 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;

@ -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:需要加载的文件列表

@ -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();
}

@ -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<Book>) BookService.getInstance().getAllBooks();
mBooks = (ArrayList<Book>) BookService.getInstance().getAllBooksNoHide();
Iterator<Book> 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();
}
}

@ -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;
}

@ -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();

@ -94,8 +94,12 @@ public class FileSystemAdapter extends BaseListAdapter<File> {
mCheckedCount = 0;
for (Map.Entry<File, Boolean> 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;

@ -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<BookGroup> 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<BookGroup> 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<Book> books = new ArrayList<>();
books.add(book);
addGroup(books, onGroup);
}
/**
* 加入批量分组
* @param mSelectBooks
* @param onGroup
*/
public void addGroup(List<Book> 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() {
}
}
}

@ -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();
}
});

@ -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();
}
}

@ -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);
}
}

@ -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

@ -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<Book> 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);
}
}
}

@ -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<ArrayList<String>>() {
@Override
public void onSuccess(ArrayList<String> 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();
});
}
}

@ -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();

@ -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;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because it is too large Load Diff

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorForeground">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="10dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@drawable/ic_fp_40px"
/>
<TextView
android:id="@+id/verify_fingerprint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="@string/verify_fingerprint"
android:textColor="@color/textPrimary"
android:textSize="16sp"
/>
<TextView
android:id="@+id/error_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:maxLines="1"
android:textSize="12sp"
android:textColor="@color/errorColor"
/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginTop="10dp"
android:background="@color/colorBackground"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/cancel"
android:background="@drawable/selector_common_bg"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="50dp"
android:gravity="center"
android:text="取消"
android:textColor="@color/textSecondary"
android:textSize="16sp"
/>
<TextView
android:id="@+id/use_pwd"
android:background="@drawable/selector_common_bg"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="50dp"
android:gravity="center"
android:text="使用密码"
android:textColor="@color/textSecondary"
android:textSize="16sp"
/>
</LinearLayout>
</LinearLayout>

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/rl_private_bookcase"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/selector_common_bg"
android:gravity="center"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/private_bookcase"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/sc_private_bookcase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:clickable="false"
android:longClickable="false" />
</RelativeLayout>
<LinearLayout
android:layout_height="15dp"
android:layout_width="match_parent" />
<LinearLayout
android:id="@+id/ll_content"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_hide_books"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/selector_common_bg"
android:orientation="vertical"
android:paddingLeft="20dp"
android:paddingTop="8dp"
android:paddingRight="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hide_books"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:text="@string/hide_books_tip"
android:textColor="@color/textAssist" />
</LinearLayout>
<RelativeLayout
android:id="@+id/rl_change_pwd"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/selector_common_bg"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/change_pwd"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_fingerprint"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/selector_common_bg"
android:gravity="center"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/fingerprint"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/sc_fingerprint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:clickable="false"
android:longClickable="false" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>

@ -6,7 +6,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<include layout="@layout/toolbar"/>
<LinearLayout
android:id="@+id/webdav_setting_webdav_url"
android:layout_width="match_parent"
@ -20,14 +19,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_url"/>
<TextView
android:id="@+id/tv_webdav_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:textColor="@color/textSecondary"
android:textColor="@color/textAssist"
tools:text="https://dav.jianguoyun.com/dav/"/>
</LinearLayout>
@ -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"/>
<TextView
android:id="@+id/tv_webdav_account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:textColor="@color/textSecondary"
android:textColor="@color/textAssist"
tools:text="输入你的WebDav账号"/>
</LinearLayout>
@ -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"/>
<TextView
android:id="@+id/tv_webdav_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:textColor="@color/textSecondary"
android:textColor="@color/textAssist"
tools:text="输入你的WebDav授权密码"/>
</LinearLayout>
@ -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"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:textColor="@color/textSecondary"
android:textColor="@color/textAssist"
android:text="@string/webdav_restore_tip"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_restore_num"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/selector_common_bg"
android:orientation="vertical"
android:paddingLeft="20dp"
android:paddingTop="8dp"
android:paddingRight="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/max_restore_list"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size" />
<TextView
android:id="@+id/tv_restore_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:text="@string/cur_restore_list_num"
android:textColor="@color/textAssist" />
</LinearLayout>
</LinearLayout>

@ -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" />
</menu>

@ -65,7 +65,7 @@
<string name="menu_bookcase_edit">编辑书架</string>
<string name="menu_bookcase_style">切换布局</string>
<string name="menu_bookcase_add">添加本地</string>
<string name="menu_bookcase_add">本地导入</string>
<string name="menu_bookcase_syn">同步书架</string>
<string name="menu_bookcase_download_all">一键缓存</string>
<string name="menu_settings">应用设置</string>
@ -280,6 +280,22 @@
<string name="import_">导入</string>
<string name="export">导出</string>
<string name="reset_layout">重置布局</string>
<string name="max_restore_list">恢复列表最大显示数</string>
<string name="cur_restore_list_num">当前显示数:%1$d</string>
<string name="private_bookcase">私密书架</string>
<string name="hide_books">隐藏书籍</string>
<string name="hide_books_tip">选择隐藏的书籍</string>
<string name="change_pwd">更改密码</string>
<string name="input_private_pwd">请输入隐私密码</string>
<string name="fingerprint">使用指纹解锁</string>
<string name="private_pwd">隐私密码</string>
<string name="set_private_pwd">设置隐私密码</string>
<string name="verify_fingerprint">请验证指纹</string>
<string name="verify_has_fingerprint">请验证已有指纹</string>
<string name="private_bookcase_tip">1、什么是私密书架?\n私密书架是设置隐私密码后自动创建的书籍分组,
此书架的书籍仅能在该分组内阅读、查看及其他操作,且阅读后不会保存上次阅读记录。\n\n2、如何进入私密书架?\n在书架页面长按左上角书架标题正确验证隐私密码即可进入私密书架。
\n\n3、如何退出私密书架?\n点击书架菜单栏切换分组按钮切换至其他分组即可退出私密书架。\n\n4、忘记隐私密码怎么办?\n隐私密码不支持找回,忘记密码后可强制关闭私密书架,并删除私密书架内所有书籍。\n\n
5、此功能处于测试阶段,如发现问题,请到我的-建议反馈里进行反馈。</string>
<string-array name="reset_screen_time">
<item>常亮</item>

@ -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

Loading…
Cancel
Save