diff --git a/.idea/misc.xml b/.idea/misc.xml index e709b95..6f853a3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -21,7 +21,7 @@ - + @@ -41,7 +41,7 @@ - + @@ -52,6 +52,7 @@ + diff --git a/README.md b/README.md index 3ca9dd4..1e7f73b 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ * 官网:[https://reader.fycz.tk](https://reader.fycz.tk) -* 反馈交流群: +* 反馈交流: * QQ群:[1085028304](https://jq.qq.com/?_wv=1027&k=6pcq8YBk) - * Telegram:[https://t.me/FYReader](https://t.me/FYReader) + * QQ频道:[点击加入](https://qun.qq.com/qqweb/qunpro/share?_wv=3&_wwv=128&inviteCode=2aP6ZQ&from=246610&biz=ka) * [更新日志](./app/src/main/assets/updatelog.fy) * [免责声明](./app/src/main/assets/disclaimer.fy) diff --git a/app/release.md b/app/release.md index e0f4764..e2efcf4 100644 --- a/app/release.md +++ b/app/release.md @@ -1,3 +1,5 @@ -* 1、开放用户登录\注册入口及同步书架(在v1.9.3版本关闭),感谢[@黑白人生]提供的服务器 -* 2、新增邮箱验证(用于找回密码),之前注册的帐号登录时需要绑定邮箱 -* 3、webdav同步书架改为从webdav恢复 \ No newline at end of file +* 1、修复部分机型阅读界面切换日夜间闪退的问题 +* 2、优化搜索界面 +* 3、搜索界面新增书架书籍搜索 +* 4、修复朗读到章节最后一页停止的问题 +* 5、新增QQ频道,建议反馈请到QQ频道进行 \ No newline at end of file diff --git a/app/src/main/assets/updatelog.fy b/app/src/main/assets/updatelog.fy index 528d152..33539ec 100644 --- a/app/src/main/assets/updatelog.fy +++ b/app/src/main/assets/updatelog.fy @@ -1,7 +1,11 @@ -2022.01.07 +2022.01.17 风月读书v2.3.2 更新内容: -1、修复MIUI13阅读界面切换日夜间闪退的问题 +1、修复部分机型阅读界面切换日夜间闪退的问题 +2、优化搜索界面 +3、搜索界面新增书架书籍搜索 +4、修复朗读到章节最后一页停止的问题 +5、新增QQ频道,建议反馈请到QQ频道进行 2021.12.11 风月读书v2.2.8 diff --git a/app/src/main/java/xyz/fycz/myreader/model/sourceAnalyzer/BookSourceManager.java b/app/src/main/java/xyz/fycz/myreader/model/sourceAnalyzer/BookSourceManager.java index 6222116..8a0eac3 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/sourceAnalyzer/BookSourceManager.java +++ b/app/src/main/java/xyz/fycz/myreader/model/sourceAnalyzer/BookSourceManager.java @@ -192,6 +192,10 @@ public class BookSourceManager { DbManager.getDaoSession().getBookSourceDao().deleteInTx(sources); } + public static boolean isBookSourceExist(BookSource source){ + if (source == null) return false; + return DbManager.getDaoSession().getBookSourceDao().load(source.getSourceUrl()) != null; + } public static String getBookSourceSort() { switch (SharedPreUtils.getInstance().getInt("SourceSort", 0)) { diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/AboutActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/AboutActivity.java index 60c44fe..7cc3fc7 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/AboutActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/AboutActivity.java @@ -96,6 +96,12 @@ public class AboutActivity extends BaseActivity { mClipboardManager.setPrimaryClip(mClipData); ToastUtils.showSuccess("邮箱\"fengyuecanzhu@gmail.com\"已复制到剪切板"); }); + binding.il.rlJoinQqChannel.setOnClickListener(v -> { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("https://qun.qq.com/qqweb/qunpro/share?_wv=3&_wwv=128&inviteCode=2aP6ZQ&from=246610&biz=ka")); + startActivity(intent); + }); + binding.il.rlShareLog.setOnClickListener(v -> DialogCreator.createCommonDialog(this, "分享崩溃日志", "你是希望将日志上传到服务器,还是直接分享给他人?", true, "上传服务器", "直接分享", (dialog, which) -> uploadCrashLog(), (dialog, which) -> shareCrashLog())); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java index 7064b1e..f2e1ad5 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java @@ -419,8 +419,10 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe pagePos = pos; saveLastChapterReadPosition(); } - screenOffTimerStart(); - initMenu(); + mHandler.post(()->{ + screenOffTimerStart(); + initMenu(); + }); recordReadTime(); if (ReadAloudService.running) { if (mPageLoader.hasChapterData(mChapters.get(mPageLoader.getChapterPos()))) { diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java index a424aa8..bbd443a 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java @@ -49,6 +49,7 @@ import xyz.fycz.myreader.entity.Setting; import xyz.fycz.myreader.greendao.entity.Book; import xyz.fycz.myreader.greendao.entity.SearchHistory; import xyz.fycz.myreader.greendao.entity.rule.BookSource; +import xyz.fycz.myreader.greendao.service.BookService; import xyz.fycz.myreader.greendao.service.SearchHistoryService; import xyz.fycz.myreader.model.SearchEngine; import xyz.fycz.myreader.model.mulvalmap.ConMVMap; @@ -65,6 +66,7 @@ import xyz.fycz.myreader.util.utils.OkHttpUtils; import xyz.fycz.myreader.util.utils.RxUtils; import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil; import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; +import xyz.fycz.myreader.widget.TagGroup; /** * @author fengyue @@ -76,14 +78,17 @@ public class SearchBookActivity extends BaseActivity { private SearchAdapter mSearchBookAdapter; private String searchKey;//搜索关键字 - private List mSearchHistories = new ArrayList<>(); - private List mSuggestions = new ArrayList<>(); - private List mHotKeys = new ArrayList<>(); + private List mSearchHistories; + private List mCurHistories = new ArrayList<>(); + private final List mBookcase = new ArrayList<>(); + private List mBooks; + private final List mSuggestions = new ArrayList<>(); + private final List mHotKeys = new ArrayList<>(); + private final List mBookcaseNames = new ArrayList<>(); + private final List mHistoryNames = new ArrayList<>(); private SearchHistoryService mSearchHistoryService; - private SearchHistoryAdapter mSearchHistoryAdapter; - private int allThreadCount; private SearchEngine searchEngine; @@ -96,13 +101,20 @@ public class SearchBookActivity extends BaseActivity { private Disposable sugDis; - private boolean showBooks; + private Disposable hisDis; + private Disposable caseDis; + + private boolean showBooks; private static String[] suggestion = {"第一序列", "大道朝天", "伏天氏", "终极斗罗", "我师兄实在太稳健了", "烂柯棋缘", "诡秘之主", "不朽凡人", "圣墟", "我是至尊", "龙王传说", "太古神王", "一念永恒", "雪鹰领主", "大主宰"}; private boolean showHot; + private boolean foldBookcase; + private boolean foldSuggest; + private boolean foldHistory; + private boolean needReGetHistory; @Override protected void bindView() { @@ -120,7 +132,7 @@ public class SearchBookActivity extends BaseActivity { @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); - if (hasFocus && binding.etSearchKey.getText().length() == 0){ + if (hasFocus && binding.etSearchKey.getText().length() == 0) { App.getHandler().postDelayed(() -> { binding.etSearchKey.requestFocus(); InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); @@ -134,7 +146,12 @@ public class SearchBookActivity extends BaseActivity { super.initData(savedInstanceState); mSetting = SysManager.getSetting(); mSearchHistoryService = SearchHistoryService.getInstance(); + mBooks = BookService.getInstance().getAllBooksNoHide(); showHot = !App.isDebug(); + foldBookcase = SharedPreUtils.getInstance().getBoolean("foldBookcase"); + foldSuggest = SharedPreUtils.getInstance().getBoolean("foldSuggest"); + foldHistory = SharedPreUtils.getInstance().getBoolean("foldHistory"); + needReGetHistory = true; searchEngine = new SearchEngine(); searchEngine.setOnSearchListener(new SearchEngine.OnSearchListener() { @Override @@ -189,18 +206,13 @@ public class SearchBookActivity extends BaseActivity { } binding.rgSearchFilter.setOnCheckedChangeListener((group, checkedId) -> { - int searchFilter; - switch (checkedId) { - case R.id.rb_all_search: - default: - searchFilter = 0; - break; - case R.id.rb_fuzzy_search: - searchFilter = 1; - break; - case R.id.rb_precise_search: - searchFilter = 2; - break; + int searchFilter = 0; + if (checkedId == R.id.rb_all_search) { + searchFilter = 0; + } else if (checkedId == R.id.rb_fuzzy_search) { + searchFilter = 1; + } else if (checkedId == R.id.rb_precise_search) { + searchFilter = 2; } mSetting.setSearchFilter(searchFilter); SysManager.saveSetting(mSetting); @@ -225,6 +237,8 @@ public class SearchBookActivity extends BaseActivity { if (StringHelper.isEmpty(searchKey)) { search(); } + searchBookcase(); + initHistoryList(); initSuggestionList(); } @@ -237,6 +251,32 @@ public class SearchBookActivity extends BaseActivity { stopSearch(); search(); }); + + binding.tgBookcase.setVisibility(foldBookcase ? View.GONE : View.VISIBLE); + binding.tvFlattenBookcase.setText(foldBookcase ? R.string.unfold : R.string.fold); + binding.tgSuggestBook.setVisibility(foldSuggest ? View.GONE : View.VISIBLE); + binding.tvFlattenSuggest.setText(foldSuggest ? R.string.unfold : R.string.fold); + binding.tgHistoryBooks.setVisibility(foldHistory ? View.GONE : View.VISIBLE); + binding.tvFlattenHistory.setText(foldHistory ? R.string.unfold : R.string.fold); + + binding.rlBookcaseBar.setOnClickListener(v -> { + foldBookcase = !foldBookcase; + binding.tgBookcase.setVisibility(foldBookcase ? View.GONE : View.VISIBLE); + binding.tvFlattenBookcase.setText(foldBookcase ? R.string.unfold : R.string.fold); + SharedPreUtils.getInstance().putBoolean("foldBookcase", foldBookcase); + }); + binding.rlSuggestBar.setOnClickListener(v -> { + foldSuggest = !foldSuggest; + binding.tgSuggestBook.setVisibility(foldSuggest ? View.GONE : View.VISIBLE); + binding.tvFlattenSuggest.setText(foldSuggest ? R.string.unfold : R.string.fold); + SharedPreUtils.getInstance().putBoolean("foldSuggest", foldSuggest); + }); + binding.rlHistoryBar.setOnClickListener(v -> { + foldHistory = !foldHistory; + binding.tgHistoryBooks.setVisibility(foldHistory ? View.GONE : View.VISIBLE); + binding.tvFlattenHistory.setText(foldHistory ? R.string.unfold : R.string.fold); + SharedPreUtils.getInstance().putBoolean("foldHistory", foldHistory); + }); initHistoryList(); } @@ -244,35 +284,43 @@ public class SearchBookActivity extends BaseActivity { protected void initClick() { super.initClick(); - //换一批点击事件 - binding.llRefreshSuggestBooks.setOnClickListener(new RenewSuggestionBook()); - //搜索按钮点击事件 binding.tvSearchConform.setOnClickListener(view -> search()); //suggestion搜索事件 - binding.tgSuggestBook.setOnTagClickListener(tag -> { + binding.tgSuggestBook.setOnTagClickListener((tag, pos) -> { binding.etSearchKey.setText(tag); binding.etSearchKey.setSelection(tag.length()); search(); }); //历史记录搜索事件 - binding.lvHistoryList.setOnItemClickListener((parent, view, position, id) -> { - binding.etSearchKey.setText(mSearchHistories.get(position).getContent()); - binding.etSearchKey.setSelection(mSearchHistories.get(position).getContent().length()); + binding.tgHistoryBooks.setOnTagClickListener((tag, pos) -> { + binding.etSearchKey.setText(tag); + binding.etSearchKey.setSelection(tag.length()); search(); }); //清空历史记录 - binding.llClearHistory.setOnClickListener(v -> { - mSearchHistoryService.clearHistory(); - initHistoryList(); + binding.tvClearHistory.setOnClickListener(v -> { + DialogCreator.createCommonDialog(this, "清除搜索记录", "确定要清除全部搜索记录吗?", + true, (dialog, which) -> { + mSearchHistoryService.clearHistory(); + initHistoryList(); + }, null); }); //清除单个历史记录 - binding.lvHistoryList.setOnItemLongClickListener((parent, view, position, id) -> { - if (mSearchHistories.get(position) != null) { - mSearchHistoryService.deleteHistory(mSearchHistories.get(position)); - initHistoryList(); + binding.tgHistoryBooks.setLongClickDelete(true); + binding.tgHistoryBooks.setOnTagChangeListener(new TagGroup.OnTagChangeListener() { + @Override + public void onAppend(TagGroup tagGroup, String tag) { + + } + + @Override + public void onDelete(TagGroup tagGroup, String tag, int pos) { + if (mCurHistories.get(pos) != null) { + mSearchHistoryService.deleteHistory(mCurHistories.get(pos)); + mSearchHistories.remove(mCurHistories.get(pos)); + } } - return true; }); binding.fabSearchStop.setOnClickListener(v -> { @@ -445,7 +493,6 @@ public class SearchBookActivity extends BaseActivity { initSuggestionList(); } }); - ; } } @@ -460,11 +507,10 @@ public class SearchBookActivity extends BaseActivity { mSuggestions.clear(); if (StringHelper.isEmpty(searchKey)) { if (mHotKeys.isEmpty()) { - binding.llSuggestBooksView.setVisibility(View.GONE); + binding.llSuggestBook.setVisibility(View.GONE); } else { - binding.llSuggestBooksView.setVisibility(View.VISIBLE); - binding.llRefreshSuggestBooks.setVisibility(View.VISIBLE); - binding.tgSuggestBook.setTags2(mHotKeys.subList(0, mHotKeys.size() / 2)); + binding.llSuggestBook.setVisibility(View.VISIBLE); + binding.tgSuggestBook.setTags2(mHotKeys); } } else { String url = "https://newzxautocmp.reader.qq.com/BookSuggAll?key=" + searchKey; @@ -480,13 +526,18 @@ public class SearchBookActivity extends BaseActivity { @Override public void onSuccess(@NotNull String s) { parseSuggListByKey(s); - binding.llRefreshSuggestBooks.setVisibility(View.GONE); + if (mSuggestions.isEmpty()) { + binding.llSuggestBook.setVisibility(View.GONE); + return; + } else { + binding.llSuggestBook.setVisibility(View.VISIBLE); + } binding.tgSuggestBook.setTags2(mSuggestions); } @Override public void onError(Throwable e) { - binding.llSuggestBooksView.setVisibility(View.GONE); + binding.llSuggestBook.setVisibility(View.GONE); } }); } @@ -527,32 +578,65 @@ public class SearchBookActivity extends BaseActivity { } } - private class RenewSuggestionBook implements View.OnClickListener { - @Override - public void onClick(View v) { - if (mHotKeys.size() > 0) { - String[] s = binding.tgSuggestBook.getTags(); - if (s[0].equals(mHotKeys.get(0))) { - binding.tgSuggestBook.setTags2(mHotKeys.subList(mHotKeys.size() / 2, mHotKeys.size())); - } else { - binding.tgSuggestBook.setTags2(mHotKeys.subList(0, mHotKeys.size() / 2)); - } - } - } - } - /** * 初始化历史列表 */ private void initHistoryList() { - mSearchHistories = mSearchHistoryService.findAllSearchHistory(); - if (mSearchHistories == null || mSearchHistories.size() == 0) { - binding.llHistoryView.setVisibility(View.GONE); - } else { - mSearchHistoryAdapter = new SearchHistoryAdapter(this, R.layout.listview_search_history_item, mSearchHistories); - binding.lvHistoryList.setAdapter(mSearchHistoryAdapter); - binding.llHistoryView.setVisibility(View.VISIBLE); + if (hisDis != null) { + hisDis.dispose(); } + Single.create((SingleOnSubscribe) emitter -> { + mSearchHistories = mSearchHistoryService.findAllSearchHistory(); + mHistoryNames.clear(); + mCurHistories.clear(); + if (mSearchHistories == null || mSearchHistories.size() == 0) { + emitter.onSuccess(false); + } else { + if (StringHelper.isEmpty(searchKey)) { + mCurHistories.addAll(mSearchHistories); + for (SearchHistory history : mSearchHistories) { + mHistoryNames.add(history.getContent()); + } + } else { + for (SearchHistory history : mSearchHistories) { + String title = history.getContent(); + int start = title.indexOf(searchKey); + if (start != -1) { + SpannableString spannableString = new SpannableString(title); + spannableString.setSpan(new ForegroundColorSpan(Color.RED), + start, start + searchKey.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + mHistoryNames.add(spannableString); + mCurHistories.add(history); + } + } + } + emitter.onSuccess(!mHistoryNames.isEmpty()); + } + }).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver() { + + @Override + public void onSubscribe(Disposable d) { + addDisposable(d); + hisDis = d; + } + + @Override + public void onSuccess(@NonNull Boolean aBoolean) { + if (aBoolean) { + binding.llHistoryBooks.setVisibility(View.VISIBLE); + binding.tgHistoryBooks.setTags2(mHistoryNames); + } else { + binding.llHistoryBooks.setVisibility(View.GONE); + } + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + binding.llHistoryBooks.setVisibility(View.GONE); + } + }); + } /** @@ -561,9 +645,67 @@ public class SearchBookActivity extends BaseActivity { private void initSearchList() { //initmBooksBean(); binding.rvSearchBooksList.setVisibility(View.VISIBLE); - binding.llSuggestBooksView.setVisibility(View.GONE); + binding.llPreKeys.setVisibility(View.GONE); } + /** + * 搜索书架 + */ + private void searchBookcase() { + if (caseDis != null) { + caseDis.dispose(); + } + if (StringHelper.isEmpty(searchKey)) { + binding.llBookcase.setVisibility(View.GONE); + } else { + Single.create((SingleOnSubscribe) emitter -> { + mBookcase.clear(); + mBookcaseNames.clear(); + for (Book book : mBooks) { + String title = book.getName() + "-" + book.getAuthor(); + if ("本地书籍".equals(book.getType())) { + title += "[本地]"; + } + int start = title.indexOf(searchKey); + if (start != -1) { + SpannableString spannableString = new SpannableString(title); + spannableString.setSpan(new ForegroundColorSpan(Color.RED), + start, start + searchKey.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + mBookcaseNames.add(spannableString); + mBookcase.add(book); + } + } + emitter.onSuccess(!mBookcase.isEmpty()); + }).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver() { + @Override + public void onSubscribe(Disposable d) { + addDisposable(d); + caseDis = d; + } + + @Override + public void onSuccess(@NonNull Boolean aBoolean) { + if (aBoolean) { + binding.llBookcase.setVisibility(View.VISIBLE); + binding.tgBookcase.setTags2(mBookcaseNames); + binding.tgBookcase.setOnTagClickListener((tag, pos) -> { + Intent intent = new Intent(SearchBookActivity.this, BookDetailedActivity.class); + BitIntentDataManager.getInstance().putData(intent, mBookcase.get(pos)); + startActivity(intent); + }); + } else { + binding.llBookcase.setVisibility(View.GONE); + } + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + binding.llBookcase.setVisibility(View.GONE); + } + }); + } + } /** * 获取搜索数据 @@ -595,7 +737,7 @@ public class SearchBookActivity extends BaseActivity { stopSearch(); binding.rpb.setIsAutoLoading(false); binding.rvSearchBooksList.setVisibility(View.GONE); - binding.llSuggestBooksView.setVisibility(View.VISIBLE); + binding.llPreKeys.setVisibility(View.VISIBLE); binding.fabSearchStop.setVisibility(View.GONE); initHistoryList(); binding.rvSearchBooksList.setAdapter(null); @@ -606,11 +748,11 @@ public class SearchBookActivity extends BaseActivity { binding.rvSearchBooksList.setAdapter(mSearchBookAdapter); binding.srlSearchBookList.setEnableRefresh(true); binding.rvSearchBooksList.setVisibility(View.VISIBLE); - binding.llSuggestBooksView.setVisibility(View.GONE); - binding.llHistoryView.setVisibility(View.GONE); + binding.llPreKeys.setVisibility(View.GONE); binding.fabSearchStop.setVisibility(View.VISIBLE); getData(); mSearchHistoryService.addOrUpadteHistory(searchKey); + needReGetHistory = true; //收起软键盘 InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(binding.etSearchKey.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java index b8681ef..018daf0 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java @@ -185,6 +185,15 @@ public class SourceEditActivity extends BaseActivity { } else if (item.getItemId() == R.id.action_clear_cookie) { DbManager.getDaoSession().getCookieBeanDao().deleteByKey(getSource().getSourceUrl()); ToastUtils.showSuccess("Cookie清除成功"); + } else if (item.getItemId() == R.id.action_delete) { + if (BookSourceManager.isBookSourceExist(source)) { + BookSourceManager.removeBookSource(source); + setResult(Activity.RESULT_OK); + ToastUtils.showSuccess("书源删除成功"); + finish(); + } else { + ToastUtils.showWarring("当前书源暂未保存,无法删除"); + } } return super.onOptionsItemSelected(item); } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/fragment/MineFragment.java b/app/src/main/java/xyz/fycz/myreader/ui/fragment/MineFragment.java index ca4e67a..8989968 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/fragment/MineFragment.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/fragment/MineFragment.java @@ -4,6 +4,7 @@ import static android.app.Activity.RESULT_OK; import android.annotation.SuppressLint; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -389,8 +390,9 @@ public class MineFragment extends BaseFragment { }); binding.mineRlFeedback.setOnClickListener(v -> { - Intent intent = new Intent(getContext(), FeedbackActivity.class); - getActivity().startActivity(intent); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("https://qun.qq.com/qqweb/qunpro/share?_wv=3&_wwv=128&inviteCode=2aP6ZQ&from=246610&biz=ka")); + startActivity(intent); }); binding.mineRlDonate.setOnClickListener(v -> startActivity(new Intent(getActivity(), DonateActivity.class))); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java index 70d6079..1a7c4ed 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java @@ -999,12 +999,15 @@ public class BookcasePresenter implements BasePresenter { if (!isAutoSyn) { DialogCreator.createTipDialog(mMainActivity, "成功将书架同步至网络!"); } + } else if (result.getCode() == 216) { + ToastUtils.showWarring("当前应用版本过旧,同步书架失败,请更新应用后重试"); } else { if (!isAutoSyn) { DialogCreator.createTipDialog(mMainActivity, "同步失败,请重试!"); } } } + @Override public void onError(Throwable e) { if (!isAutoSyn) { diff --git a/app/src/main/java/xyz/fycz/myreader/widget/TagGroup.java b/app/src/main/java/xyz/fycz/myreader/widget/TagGroup.java index 472d708..638ec6c 100644 --- a/app/src/main/java/xyz/fycz/myreader/widget/TagGroup.java +++ b/app/src/main/java/xyz/fycz/myreader/widget/TagGroup.java @@ -1,5 +1,6 @@ package xyz.fycz.myreader.widget; +import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; @@ -32,6 +33,7 @@ import java.util.ArrayList; import java.util.List; import xyz.fycz.myreader.R; +import xyz.fycz.myreader.widget.explosion_field.ExplosionField; /** * A TagGroup is a special layout with a set of tags. @@ -70,6 +72,9 @@ public class TagGroup extends ViewGroup { private final float default_horizontal_padding; private final float default_vertical_padding; + /** Indicate whether the tags are deleted by holding down. Default is false. */ + private boolean isLongClickDelete; + /** Indicates whether this TagGroup is set up to APPEND mode or DISPLAY mode. Default is false. */ private boolean isAppendMode; @@ -136,6 +141,8 @@ public class TagGroup extends ViewGroup { /** Listener used to handle tag click event. */ private InternalTagClickListener mInternalTagClickListener = new InternalTagClickListener(); + private ExplosionField mExplosionField; + public TagGroup(Context context) { this(context, null); } @@ -156,6 +163,7 @@ public class TagGroup extends ViewGroup { // Load styled attributes. final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TagGroup, defStyleAttr, R.style.TagGroup); try { + isLongClickDelete = a.getBoolean(R.styleable.TagGroup_atg_isLongClickDelete, false); isAppendMode = a.getBoolean(R.styleable.TagGroup_atg_isAppendMode, false); inputHint = a.getText(R.styleable.TagGroup_atg_inputHint); borderColor = a.getColor(R.styleable.TagGroup_atg_borderColor, default_border_color); @@ -193,6 +201,18 @@ public class TagGroup extends ViewGroup { } } + @Override + protected void onAttachedToWindow() { + mExplosionField = ExplosionField.attach2Window((Activity) getContext()); + super.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + mExplosionField.clear(); + super.onDetachedFromWindow(); + } + /** * Call this to submit the INPUT tag. */ @@ -492,8 +512,15 @@ public class TagGroup extends ViewGroup { throw new IllegalStateException("Already has a INPUT tag in group."); } - final TagView newInputTag = new TagView(getContext(), TagView.STATE_INPUT, tag); + final TagView newInputTag = new TagView(getContext(), TagView.STATE_INPUT, tag, getChildCount()); newInputTag.setOnClickListener(mInternalTagClickListener); + newInputTag.setOnLongClickListener(v -> { + if (isLongClickDelete) { + deleteTag(newInputTag); + return true; + } + return false; + }); addView(newInputTag); } @@ -503,8 +530,15 @@ public class TagGroup extends ViewGroup { * @param tag the tag to append. */ protected void appendTag(CharSequence tag) { - final TagView newTag = new TagView(getContext(), TagView.STATE_NORMAL, tag); + final TagView newTag = new TagView(getContext(), TagView.STATE_NORMAL, tag, getChildCount()); newTag.setOnClickListener(mInternalTagClickListener); + newTag.setOnLongClickListener(v -> { + if (isLongClickDelete) { + deleteTag(newTag); + return true; + } + return false; + }); addView(newTag); } @@ -532,10 +566,19 @@ public class TagGroup extends ViewGroup { mOnTagClickListener = l; } + /** + * Set up whether the tags are deleted by holding down. + * @param longClickDelete + */ + public void setLongClickDelete(boolean longClickDelete) { + isLongClickDelete = longClickDelete; + } + protected void deleteTag(TagView tagView) { + mExplosionField.explode(tagView); removeView(tagView); if (mOnTagChangeListener != null) { - mOnTagChangeListener.onDelete(TagGroup.this, tagView.getText().toString()); + mOnTagChangeListener.onDelete(TagGroup.this, tagView.getText().toString(), tagView.pos); } } @@ -555,7 +598,7 @@ public class TagGroup extends ViewGroup { * * @param tag the deleted tag. */ - void onDelete(TagGroup tagGroup, String tag); + void onDelete(TagGroup tagGroup, String tag, int pos); } /** @@ -567,7 +610,7 @@ public class TagGroup extends ViewGroup { * * @param tag The tag text of the tag that was clicked. */ - void onTagClick(String tag); + void onTagClick(String tag, int pos); } /** @@ -630,6 +673,7 @@ public class TagGroup extends ViewGroup { * The tag view click listener for internal use. */ class InternalTagClickListener implements OnClickListener { + @Override public void onClick(View v) { final TagView tag = (TagView) v; @@ -656,7 +700,7 @@ public class TagGroup extends ViewGroup { } } else { if (mOnTagClickListener != null) { - mOnTagClickListener.onTagClick(tag.getText().toString()); + mOnTagClickListener.onTagClick(tag.getText().toString(), tag.pos); } } } @@ -669,6 +713,9 @@ public class TagGroup extends ViewGroup { public static final int STATE_NORMAL = 1; public static final int STATE_INPUT = 2; + /** The position in the group. */ + private int pos = -1; + /** The offset to the text. */ private static final int CHECKED_MARKER_OFFSET = 3; @@ -724,8 +771,9 @@ public class TagGroup extends ViewGroup { } - public TagView(Context context, final int state, CharSequence text) { + public TagView(Context context, final int state, CharSequence text, int pos) { super(context); + this.pos = pos; setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding); setLayoutParams(new LayoutParams( LayoutParams.WRAP_CONTENT, @@ -788,7 +836,7 @@ public class TagGroup extends ViewGroup { if (lastNormalTagView.isChecked) { removeView(lastNormalTagView); if (mOnTagChangeListener != null) { - mOnTagChangeListener.onDelete(TagGroup.this, lastNormalTagView.getText().toString()); + mOnTagChangeListener.onDelete(TagGroup.this, lastNormalTagView.getText().toString(), pos); } } else { final TagView checkedTagView = getCheckedTag(); @@ -1003,6 +1051,7 @@ public class TagGroup extends ViewGroup { } break; } + case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: { isPressed = false; invalidatePaint(); diff --git a/app/src/main/java/xyz/fycz/myreader/widget/explosion_field/ExplosionAnimator.java b/app/src/main/java/xyz/fycz/myreader/widget/explosion_field/ExplosionAnimator.java new file mode 100644 index 0000000..91da4d2 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/widget/explosion_field/ExplosionAnimator.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2015 tyrantgit + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package xyz.fycz.myreader.widget.explosion_field; + +import android.animation.ValueAnimator; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Interpolator; + +import java.util.Random; + +public class ExplosionAnimator extends ValueAnimator { + + static long DEFAULT_DURATION = 0x400; + private static final Interpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator(0.6f); + private static final float END_VALUE = 1.4f; + private static final float X = Utils.dp2Px(5); + private static final float Y = Utils.dp2Px(20); + private static final float V = Utils.dp2Px(2); + private static final float W = Utils.dp2Px(1); + private Paint mPaint; + private Particle[] mParticles; + private Rect mBound; + private View mContainer; + + public ExplosionAnimator(View container, Bitmap bitmap, Rect bound) { + mPaint = new Paint(); + mBound = new Rect(bound); + int partLen = 15; + mParticles = new Particle[partLen * partLen]; + Random random = new Random(System.currentTimeMillis()); + int w = bitmap.getWidth() / (partLen + 2); + int h = bitmap.getHeight() / (partLen + 2); + for (int i = 0; i < partLen; i++) { + for (int j = 0; j < partLen; j++) { + mParticles[(i * partLen) + j] = generateParticle(bitmap.getPixel((j + 1) * w, (i + 1) * h), random); + } + } + mContainer = container; + setFloatValues(0f, END_VALUE); + setInterpolator(DEFAULT_INTERPOLATOR); + setDuration(DEFAULT_DURATION); + } + + private Particle generateParticle(int color, Random random) { + Particle particle = new Particle(); + particle.color = color; + particle.radius = V; + if (random.nextFloat() < 0.2f) { + particle.baseRadius = V + ((X - V) * random.nextFloat()); + } else { + particle.baseRadius = W + ((V - W) * random.nextFloat()); + } + float nextFloat = random.nextFloat(); + particle.top = mBound.height() * ((0.18f * random.nextFloat()) + 0.2f); + particle.top = nextFloat < 0.2f ? particle.top : particle.top + ((particle.top * 0.2f) * random.nextFloat()); + particle.bottom = (mBound.height() * (random.nextFloat() - 0.5f)) * 1.8f; + float f = nextFloat < 0.2f ? particle.bottom : nextFloat < 0.8f ? particle.bottom * 0.6f : particle.bottom * 0.3f; + particle.bottom = f; + particle.mag = 4.0f * particle.top / particle.bottom; + particle.neg = (-particle.mag) / particle.bottom; + f = mBound.centerX() + (Y * (random.nextFloat() - 0.5f)); + particle.baseCx = f; + particle.cx = f; + f = mBound.centerY() + (Y * (random.nextFloat() - 0.5f)); + particle.baseCy = f; + particle.cy = f; + particle.life = END_VALUE / 10 * random.nextFloat(); + particle.overflow = 0.4f * random.nextFloat(); + particle.alpha = 1f; + return particle; + } + + public boolean draw(Canvas canvas) { + if (!isStarted()) { + return false; + } + for (Particle particle : mParticles) { + particle.advance((float) getAnimatedValue()); + if (particle.alpha > 0f) { + mPaint.setColor(particle.color); + mPaint.setAlpha((int) (Color.alpha(particle.color) * particle.alpha)); + canvas.drawCircle(particle.cx, particle.cy, particle.radius, mPaint); + } + } + mContainer.invalidate(); + return true; + } + + @Override + public void start() { + super.start(); + mContainer.invalidate(mBound); + } + + private class Particle { + float alpha; + int color; + float cx; + float cy; + float radius; + float baseCx; + float baseCy; + float baseRadius; + float top; + float bottom; + float mag; + float neg; + float life; + float overflow; + + + public void advance(float factor) { + float f = 0f; + float normalization = factor / END_VALUE; + if (normalization < life || normalization > 1f - overflow) { + alpha = 0f; + return; + } + normalization = (normalization - life) / (1f - life - overflow); + float f2 = normalization * END_VALUE; + if (normalization >= 0.7f) { + f = (normalization - 0.7f) / 0.3f; + } + alpha = 1f - f; + f = bottom * f2; + cx = baseCx + f; + cy = (float) (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag; + radius = V + (baseRadius - V) * f2; + } + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/widget/explosion_field/ExplosionField.java b/app/src/main/java/xyz/fycz/myreader/widget/explosion_field/ExplosionField.java new file mode 100644 index 0000000..9597026 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/widget/explosion_field/ExplosionField.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2015 tyrantgit + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package xyz.fycz.myreader.widget.explosion_field; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.media.MediaPlayer; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + + +public class ExplosionField extends View { + + private long customDuration = ExplosionAnimator.DEFAULT_DURATION; + private int idPlayAnimationEffect = 0; + private OnAnimatorListener mZAnimatorListener; + private OnClickListener mOnClickListener; + + private List mExplosions = new ArrayList<>(); + private int[] mExpandInset = new int[2]; + + public ExplosionField(Context context) { + super(context); + init(); + } + + public ExplosionField(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public ExplosionField(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + + Arrays.fill(mExpandInset, Utils.dp2Px(32)); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + for (ExplosionAnimator explosion : mExplosions) { + explosion.draw(canvas); + } + } + + public void playSoundAnimationEffect(int id) { + this.idPlayAnimationEffect = id; + } + + public void setCustomDuration(long customDuration) { + this.customDuration = customDuration; + } + + public void addActionEvent(OnAnimatorListener ievents) { + this.mZAnimatorListener = ievents; + } + + + public void expandExplosionBound(int dx, int dy) { + mExpandInset[0] = dx; + mExpandInset[1] = dy; + } + + public void explode(Bitmap bitmap, Rect bound, long startDelay) { + explode(bitmap, bound, startDelay, null); + } + + public void explode(Bitmap bitmap, Rect bound, long startDelay, final View view) { + long currentDuration = customDuration; + final ExplosionAnimator explosion = new ExplosionAnimator(this, bitmap, bound); + explosion.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mExplosions.remove(animation); + if (view != null) { + view.setScaleX(1); + view.setScaleY(1); + view.setAlpha(1); + view.setOnClickListener(mOnClickListener);//set event + + } + } + }); + explosion.setStartDelay(startDelay); + explosion.setDuration(currentDuration); + mExplosions.add(explosion); + explosion.start(); + } + + public void explode(View view) { + explode(view, false); + } + + public void explode(final View view, Boolean restartState) { + + Rect r = new Rect(); + view.getGlobalVisibleRect(r); + int[] location = new int[2]; + getLocationOnScreen(location); +// getLocationInWindow(location); +// view.getLocationInWindow(location); + r.offset(-location[0], -location[1]); + r.inset(-mExpandInset[0], -mExpandInset[1]); + int startDelay = 100; + ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(150); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + + Random random = new Random(); + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + view.setTranslationX((random.nextFloat() - 0.5f) * view.getWidth() * 0.05f); + view.setTranslationY((random.nextFloat() - 0.5f) * view.getHeight() * 0.05f); + } + }); + + animator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animator) { + if (idPlayAnimationEffect != 0) + MediaPlayer.create(getContext(), idPlayAnimationEffect).start(); + } + + @Override + public void onAnimationEnd(Animator animator) { + if (mZAnimatorListener != null) { + mZAnimatorListener.onAnimationEnd(animator, ExplosionField.this); + } + } + + @Override + public void onAnimationCancel(Animator animator) { + Log.i("PRUEBA", "CANCEL"); + } + + @Override + public void onAnimationRepeat(Animator animator) { + Log.i("PRUEBA", "REPEAT"); + } + }); + + animator.start(); + view.animate().setDuration(150).setStartDelay(startDelay).scaleX(0f).scaleY(0f).alpha(0f).start(); + if (restartState) + explode(Utils.createBitmapFromView(view), r, startDelay, view); + else + explode(Utils.createBitmapFromView(view), r, startDelay); + + } + + public void clear() { + mExplosions.clear(); + invalidate(); + } + + public static ExplosionField attach2Window(Activity activity) { + ViewGroup rootView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT); + ExplosionField explosionField = new ExplosionField(activity); + rootView.addView(explosionField, new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + return explosionField; + } + + public void setOnClickListener(OnClickListener mOnClickListener) { + this.mOnClickListener = mOnClickListener; + } + + +} diff --git a/app/src/main/java/xyz/fycz/myreader/widget/explosion_field/OnAnimatorListener.java b/app/src/main/java/xyz/fycz/myreader/widget/explosion_field/OnAnimatorListener.java new file mode 100644 index 0000000..571c2c9 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/widget/explosion_field/OnAnimatorListener.java @@ -0,0 +1,8 @@ +package xyz.fycz.myreader.widget.explosion_field; + +import android.animation.Animator; +import android.view.View; + +public interface OnAnimatorListener { + void onAnimationEnd(Animator animator, View view); +} diff --git a/app/src/main/java/xyz/fycz/myreader/widget/explosion_field/Utils.java b/app/src/main/java/xyz/fycz/myreader/widget/explosion_field/Utils.java new file mode 100644 index 0000000..f41bbb6 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/widget/explosion_field/Utils.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2015 tyrantgit + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package xyz.fycz.myreader.widget.explosion_field; + + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.ImageView; + +public class Utils { + + private Utils() { + } + + private static final float DENSITY = Resources.getSystem().getDisplayMetrics().density; + private static final Canvas sCanvas = new Canvas(); + + public static int dp2Px(int dp) { + return Math.round(dp * DENSITY); + } + + public static Bitmap createBitmapFromView(View view) { + if (view instanceof ImageView) { + Drawable drawable = ((ImageView) view).getDrawable(); + if (drawable != null && drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } + } + view.clearFocus(); + Bitmap bitmap = createBitmapSafely(view.getWidth(), + view.getHeight(), Bitmap.Config.ARGB_8888, 1); + if (bitmap != null) { + synchronized (sCanvas) { + Canvas canvas = sCanvas; + canvas.setBitmap(bitmap); + view.draw(canvas); + canvas.setBitmap(null); + } + } + return bitmap; + } + + public static Bitmap createBitmapSafely(int width, int height, Bitmap.Config config, int retryCount) { + try { + return Bitmap.createBitmap(width, height, config); + } catch (OutOfMemoryError e) { + e.printStackTrace(); + if (retryCount > 0) { + System.gc(); + return createBitmapSafely(width, height, config, retryCount - 1); + } + return null; + } + } +} diff --git a/app/src/main/res/layout/activity_search_book.xml b/app/src/main/res/layout/activity_search_book.xml index 41a1334..d9f2477 100644 --- a/app/src/main/res/layout/activity_search_book.xml +++ b/app/src/main/res/layout/activity_search_book.xml @@ -87,102 +87,168 @@ android:layout_height="3dp" android:visibility="visible" /> - - - + android:layout_height="wrap_content"> + android:orientation="vertical" + android:padding="5dp"> - + android:orientation="vertical" + android:paddingHorizontal="5dp" + android:visibility="gone"> - + + + + + + + + + + + - - - - - - - - - - - + android:orientation="vertical" + android:paddingHorizontal="5dp" + android:visibility="gone"> - - - - - + + + + + + + + + + + + android:orientation="vertical" + android:paddingHorizontal="5dp" + android:visibility="visible"> + + + + + + + + + + + + + + - - + + app:layout_anchorGravity="right|bottom" /> diff --git a/app/src/main/res/layout/layout_about_content.xml b/app/src/main/res/layout/layout_about_content.xml index f33a1f2..bdaa5ed 100644 --- a/app/src/main/res/layout/layout_about_content.xml +++ b/app/src/main/res/layout/layout_about_content.xml @@ -15,8 +15,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorForeground" - android:orientation="vertical" - > + android:orientation="vertical"> + + + + + + + + + - - + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index b22bd86..781aed0 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,6 +1,6 @@ - #FF2196F3 + @color/blue_bar #3F51B5 #90cbf6 @@ -102,6 +102,7 @@ #18188a + #FF2196F3 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d4b07bc..538ed73 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -108,6 +108,7 @@ https://github.com/fengyuecanzhu/FYReader 检查更新 致谢名单 + 加入QQ频道 @@ -150,7 +151,7 @@ 搜索 主题模式 加入QQ群 - 建议反馈 + 建议反馈(QQ频道) 分组切换 分组管理 设置分组 @@ -519,6 +520,11 @@ 清除搜索结果 发送验证码 重新发送%s + 推荐 + 搜索记录(长按删除) + 清除搜索记录 + 收起 + 展开 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index d1db0a2..e71cad8 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -273,6 +273,7 @@ + diff --git a/app/version_code.properties b/app/version_code.properties index e314e56..a628e66 100644 --- a/app/version_code.properties +++ b/app/version_code.properties @@ -1,3 +1,3 @@ #Fri Jun 18 21:45:31 CST 2021 -VERSION_CODE=231 -NEED_CREATE_RELEASE=false +VERSION_CODE=232 +NEED_CREATE_RELEASE=true