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 |
||||
VERSION_CODE=177 |
||||
#Sat Jan 09 20:56:54 CST 2021 |
||||
VERSION_CODE=178 |
||||
|
Loading…
Reference in new issue