parent
d0e5b5cd72
commit
9ffec429a7
Binary file not shown.
@ -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() { |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
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> |
@ -1,2 +1,2 @@ |
|||||||
#Fri Jan 08 23:11:19 CST 2021 |
#Sat Jan 09 20:56:54 CST 2021 |
||||||
VERSION_CODE=177 |
VERSION_CODE=178 |
||||||
|
Loading…
Reference in new issue