parent
36a2647ccb
commit
44e0be9ab8
Binary file not shown.
@ -1,7 +1,7 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
<?xml version="1.0" encoding="UTF-8"?> |
||||||
<project version="4"> |
<project version="4"> |
||||||
<component name="SqlDialectMappings"> |
<component name="SqlDialectMappings"> |
||||||
<file url="file://$PROJECT_DIR$/app/src/main/java/xyz/fycz/myreader/backup/UserService.java" dialect="GenericSQL" /> |
<file url="file://$PROJECT_DIR$/app/src/main/java/xyz/fycz/myreader/model/backup/UserService.java" dialect="GenericSQL" /> |
||||||
<file url="PROJECT" dialect="MySQL" /> |
<file url="PROJECT" dialect="MySQL" /> |
||||||
</component> |
</component> |
||||||
</project> |
</project> |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,63 +0,0 @@ |
|||||||
package xyz.fycz.myreader.controller; |
|
||||||
|
|
||||||
import xyz.fycz.myreader.greendao.entity.Book; |
|
||||||
import xyz.fycz.myreader.greendao.entity.Chapter; |
|
||||||
import xyz.fycz.myreader.greendao.service.ChapterService; |
|
||||||
|
|
||||||
import java.util.ArrayList; |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class SyncChaptersController { |
|
||||||
|
|
||||||
private ArrayList<Chapter> mLocalChapters;//本地章节
|
|
||||||
private ArrayList<Chapter> mNetChapters;//网络章节
|
|
||||||
private Book mBook;//书
|
|
||||||
private ChapterService mChapterService; |
|
||||||
|
|
||||||
public SyncChaptersController(ArrayList<Chapter> localChapters,ArrayList<Chapter> netChapters,Book book){ |
|
||||||
mLocalChapters = localChapters; |
|
||||||
mNetChapters = netChapters; |
|
||||||
mBook = book; |
|
||||||
mChapterService = ChapterService.getInstance(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 开始同步 |
|
||||||
*/ |
|
||||||
public void sync(){ |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 更新所有章节 |
|
||||||
* |
|
||||||
* @param newChapters |
|
||||||
*/ |
|
||||||
private void updateAllOldChapterData(ArrayList<Chapter> newChapters) { |
|
||||||
int i; |
|
||||||
for (i = 0; i < mLocalChapters.size() && i < newChapters.size(); i++) { |
|
||||||
Chapter oldChapter = mLocalChapters.get(i); |
|
||||||
Chapter newChapter = newChapters.get(i); |
|
||||||
if (!oldChapter.getTitle().equals(newChapter.getTitle())) { |
|
||||||
oldChapter.setTitle(newChapter.getTitle()); |
|
||||||
oldChapter.setUrl(newChapter.getUrl()); |
|
||||||
oldChapter.setContent(null); |
|
||||||
mChapterService.updateEntity(oldChapter); |
|
||||||
} |
|
||||||
} |
|
||||||
if (mLocalChapters.size() < newChapters.size()) { |
|
||||||
for (int j = mLocalChapters.size(); j < newChapters.size(); j++) { |
|
||||||
|
|
||||||
mLocalChapters.add(newChapters.get(j)); |
|
||||||
mChapterService.addChapter(newChapters.get(j), null); |
|
||||||
} |
|
||||||
} else if (mLocalChapters.size() > newChapters.size()) { |
|
||||||
for (int j = newChapters.size(); j < mLocalChapters.size(); j++) { |
|
||||||
mChapterService.deleteEntity(mLocalChapters.get(j)); |
|
||||||
} |
|
||||||
mLocalChapters.subList(0, newChapters.size()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,12 +0,0 @@ |
|||||||
package xyz.fycz.myreader.creator; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author fengyue |
|
||||||
* @date 2020/7/15 8:02 |
|
||||||
*/ |
|
||||||
public class ListenerInterface { |
|
||||||
public interface OnRefreshFinishListener{ |
|
||||||
void onRefresh(); |
|
||||||
void onFinish(); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,253 @@ |
|||||||
|
package xyz.fycz.myreader.model; |
||||||
|
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import io.reactivex.Observer; |
||||||
|
import io.reactivex.Scheduler; |
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers; |
||||||
|
import io.reactivex.disposables.CompositeDisposable; |
||||||
|
import io.reactivex.disposables.Disposable; |
||||||
|
import io.reactivex.schedulers.Schedulers; |
||||||
|
import xyz.fycz.myreader.entity.SearchBookBean; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; |
||||||
|
import xyz.fycz.myreader.util.SharedPreUtils; |
||||||
|
import xyz.fycz.myreader.util.ToastUtils; |
||||||
|
import xyz.fycz.myreader.webapi.CommonApi; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; |
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException; |
||||||
|
import java.net.URLEncoder; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
|
||||||
|
|
||||||
|
public class SearchEngine { |
||||||
|
|
||||||
|
private static final String TAG = "SearchEngine"; |
||||||
|
|
||||||
|
//线程池
|
||||||
|
private ExecutorService executorService; |
||||||
|
|
||||||
|
private Scheduler scheduler; |
||||||
|
private CompositeDisposable compositeDisposable; |
||||||
|
|
||||||
|
private List<ReadCrawler> mSourceList = new ArrayList<>(); |
||||||
|
|
||||||
|
private int threadsNum; |
||||||
|
private int searchSiteIndex; |
||||||
|
private int searchSuccessNum; |
||||||
|
private int searchFinishNum; |
||||||
|
|
||||||
|
private OnSearchListener searchListener; |
||||||
|
|
||||||
|
public SearchEngine() { |
||||||
|
threadsNum = SharedPreUtils.getInstance().getInt("threadNum", 8); |
||||||
|
} |
||||||
|
|
||||||
|
public void setOnSearchListener(OnSearchListener searchListener) { |
||||||
|
this.searchListener = searchListener; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索引擎初始化 |
||||||
|
*/ |
||||||
|
public void initSearchEngine(@NonNull List<ReadCrawler> sourceList) { |
||||||
|
mSourceList.addAll(sourceList); |
||||||
|
executorService = Executors.newFixedThreadPool(threadsNum); |
||||||
|
scheduler = Schedulers.from(executorService); |
||||||
|
compositeDisposable = new CompositeDisposable(); |
||||||
|
} |
||||||
|
|
||||||
|
public void stopSearch() { |
||||||
|
if (compositeDisposable != null) compositeDisposable.dispose(); |
||||||
|
compositeDisposable = new CompositeDisposable(); |
||||||
|
searchListener.loadMoreFinish(true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 刷新引擎 |
||||||
|
* |
||||||
|
* @param sourceList |
||||||
|
*/ |
||||||
|
public void refreshSearchEngine(@NonNull List<ReadCrawler> sourceList) { |
||||||
|
mSourceList.clear(); |
||||||
|
mSourceList.addAll(sourceList); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 关闭引擎 |
||||||
|
*/ |
||||||
|
public void closeSearchEngine() { |
||||||
|
executorService.shutdown(); |
||||||
|
if (!compositeDisposable.isDisposed()) |
||||||
|
compositeDisposable.dispose(); |
||||||
|
compositeDisposable = null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 搜索关键字 |
||||||
|
* |
||||||
|
* @param keyword |
||||||
|
*/ |
||||||
|
public void search(String keyword) { |
||||||
|
if (mSourceList.size() == 0) { |
||||||
|
ToastUtils.showWarring("当前书源已全部禁用,无法搜索!"); |
||||||
|
searchListener.loadMoreFinish(true); |
||||||
|
return; |
||||||
|
} |
||||||
|
searchSuccessNum = 0; |
||||||
|
searchSiteIndex = -1; |
||||||
|
searchFinishNum = 0; |
||||||
|
for (int i = 0; i < threadsNum; i++) { |
||||||
|
searchOnEngine(keyword); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据书名和作者搜索书籍 |
||||||
|
* |
||||||
|
* @param title |
||||||
|
* @param author |
||||||
|
*/ |
||||||
|
public void search(String title, String author) { |
||||||
|
if (mSourceList.size() == 0) { |
||||||
|
ToastUtils.showWarring("当前书源已全部禁用,无法搜索!"); |
||||||
|
searchListener.loadMoreFinish(true); |
||||||
|
return; |
||||||
|
} |
||||||
|
searchSuccessNum = 0; |
||||||
|
searchSiteIndex = -1; |
||||||
|
searchFinishNum = 0; |
||||||
|
for (int i = 0; i < threadsNum; i++) { |
||||||
|
searchOnEngine(title, author); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private synchronized void searchOnEngine(String keyword) { |
||||||
|
searchSiteIndex++; |
||||||
|
if (searchSiteIndex < mSourceList.size()) { |
||||||
|
ReadCrawler crawler = mSourceList.get(searchSiteIndex); |
||||||
|
String searchKey = keyword; |
||||||
|
if (crawler.getSearchCharset().toLowerCase().equals("gbk")) { |
||||||
|
try { |
||||||
|
searchKey = URLEncoder.encode(keyword, crawler.getSearchCharset()); |
||||||
|
} catch (UnsupportedEncodingException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
CommonApi.search(searchKey, crawler) |
||||||
|
.subscribeOn(scheduler) |
||||||
|
.observeOn(AndroidSchedulers.mainThread()) |
||||||
|
.subscribe(new Observer<ConcurrentMultiValueMap<SearchBookBean, Book>>() { |
||||||
|
@Override |
||||||
|
public void onSubscribe(Disposable d) { |
||||||
|
compositeDisposable.add(d); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onNext(ConcurrentMultiValueMap<SearchBookBean, Book> bookSearchBeans) { |
||||||
|
searchFinishNum++; |
||||||
|
if (bookSearchBeans != null) { |
||||||
|
searchSuccessNum++; |
||||||
|
searchListener.loadMoreSearchBook(bookSearchBeans); |
||||||
|
} |
||||||
|
searchOnEngine(keyword); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onError(Throwable e) { |
||||||
|
searchFinishNum++; |
||||||
|
searchOnEngine(keyword); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onComplete() { |
||||||
|
|
||||||
|
} |
||||||
|
}); |
||||||
|
} else { |
||||||
|
if (searchFinishNum >= mSourceList.size()) { |
||||||
|
if (searchSuccessNum == 0) { |
||||||
|
searchListener.searchBookError(new Throwable("未搜索到内容")); |
||||||
|
} |
||||||
|
searchListener.loadMoreFinish(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private synchronized void searchOnEngine(final String title, final String author) { |
||||||
|
searchSiteIndex++; |
||||||
|
if (searchSiteIndex < mSourceList.size()) { |
||||||
|
ReadCrawler crawler = mSourceList.get(searchSiteIndex); |
||||||
|
String searchKey = title; |
||||||
|
if (crawler.getSearchCharset().toLowerCase().equals("gbk")) { |
||||||
|
try { |
||||||
|
searchKey = URLEncoder.encode(title, crawler.getSearchCharset()); |
||||||
|
} catch (UnsupportedEncodingException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
CommonApi.search(searchKey, crawler) |
||||||
|
.subscribeOn(scheduler) |
||||||
|
.observeOn(AndroidSchedulers.mainThread()) |
||||||
|
.subscribe(new Observer<ConcurrentMultiValueMap<SearchBookBean, Book>>() { |
||||||
|
@Override |
||||||
|
public void onSubscribe(Disposable d) { |
||||||
|
compositeDisposable.add(d); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onNext(ConcurrentMultiValueMap<SearchBookBean, Book> bookSearchBeans) { |
||||||
|
searchFinishNum++; |
||||||
|
if (bookSearchBeans != null) { |
||||||
|
List<Book> books = bookSearchBeans.getValues(new SearchBookBean(title, author)); |
||||||
|
if (books != null) { |
||||||
|
searchSuccessNum++; |
||||||
|
searchListener.loadMoreSearchBook(books); |
||||||
|
} |
||||||
|
} |
||||||
|
searchOnEngine(title, author); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onError(Throwable e) { |
||||||
|
searchFinishNum++; |
||||||
|
searchOnEngine(title, author); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onComplete() { |
||||||
|
|
||||||
|
} |
||||||
|
}); |
||||||
|
} else { |
||||||
|
if (searchFinishNum >= mSourceList.size()) { |
||||||
|
if (searchSuccessNum == 0) { |
||||||
|
searchListener.searchBookError(new Throwable("未搜索到内容")); |
||||||
|
} |
||||||
|
searchListener.loadMoreFinish(true); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/ |
||||||
|
public interface OnSearchListener { |
||||||
|
|
||||||
|
void loadMoreFinish(Boolean isAll); |
||||||
|
|
||||||
|
void loadMoreSearchBook(ConcurrentMultiValueMap<SearchBookBean, Book> items); |
||||||
|
|
||||||
|
void loadMoreSearchBook(List<Book> items); |
||||||
|
|
||||||
|
void searchBookError(Throwable throwable); |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -1,7 +1,7 @@ |
|||||||
package xyz.fycz.myreader.backup; |
package xyz.fycz.myreader.model.backup; |
||||||
|
|
||||||
import xyz.fycz.myreader.application.MyApplication; |
import xyz.fycz.myreader.application.MyApplication; |
||||||
import xyz.fycz.myreader.callback.ResultCallback; |
import xyz.fycz.myreader.webapi.callback.ResultCallback; |
||||||
import xyz.fycz.myreader.common.APPCONST; |
import xyz.fycz.myreader.common.APPCONST; |
||||||
import xyz.fycz.myreader.common.URLCONST; |
import xyz.fycz.myreader.common.URLCONST; |
||||||
import xyz.fycz.myreader.util.*; |
import xyz.fycz.myreader.util.*; |
@ -1,4 +1,4 @@ |
|||||||
package xyz.fycz.myreader.mulvalmap; |
package xyz.fycz.myreader.model.mulvalmap; |
||||||
|
|
||||||
import java.util.ArrayList; |
import java.util.ArrayList; |
||||||
import java.util.LinkedHashMap; |
import java.util.LinkedHashMap; |
@ -1,4 +1,4 @@ |
|||||||
package xyz.fycz.myreader.mulvalmap; |
package xyz.fycz.myreader.model.mulvalmap; |
||||||
|
|
||||||
import java.util.List; |
import java.util.List; |
||||||
import java.util.Map; |
import java.util.Map; |
@ -1,191 +1,81 @@ |
|||||||
package xyz.fycz.myreader.ui.adapter; |
package xyz.fycz.myreader.ui.adapter; |
||||||
|
|
||||||
import android.app.Activity; |
import android.app.Activity; |
||||||
import android.content.Context; |
import android.text.TextUtils; |
||||||
import android.os.Handler; |
|
||||||
import android.view.LayoutInflater; |
|
||||||
import android.view.View; |
|
||||||
import android.view.ViewGroup; |
|
||||||
import android.widget.ImageView; |
|
||||||
import android.widget.TextView; |
|
||||||
|
|
||||||
import androidx.annotation.NonNull; |
|
||||||
import androidx.annotation.Nullable; |
|
||||||
import com.bumptech.glide.Glide; |
|
||||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners; |
|
||||||
import com.bumptech.glide.request.RequestOptions; |
|
||||||
import xyz.fycz.myreader.R; |
|
||||||
import xyz.fycz.myreader.application.MyApplication; |
import xyz.fycz.myreader.application.MyApplication; |
||||||
import xyz.fycz.myreader.callback.ResultCallback; |
import xyz.fycz.myreader.base.adapter.BaseListAdapter; |
||||||
import xyz.fycz.myreader.webapi.crawler.BookInfoCrawler; |
import xyz.fycz.myreader.base.adapter.IViewHolder; |
||||||
import xyz.fycz.myreader.webapi.crawler.ReadCrawler; |
|
||||||
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil; |
|
||||||
import xyz.fycz.myreader.custom.DragAdapter; |
|
||||||
import xyz.fycz.myreader.entity.SearchBookBean; |
import xyz.fycz.myreader.entity.SearchBookBean; |
||||||
import xyz.fycz.myreader.enums.BookSource; |
|
||||||
import xyz.fycz.myreader.greendao.entity.Book; |
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap; |
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; |
||||||
import xyz.fycz.myreader.util.StringHelper; |
import xyz.fycz.myreader.ui.adapter.holder.SearchBookHolder; |
||||||
import xyz.fycz.myreader.webapi.CommonApi; |
|
||||||
|
|
||||||
import java.util.ArrayList; |
import java.util.ArrayList; |
||||||
import java.util.HashMap; |
|
||||||
import java.util.List; |
import java.util.List; |
||||||
|
|
||||||
|
|
||||||
/** |
/** |
||||||
* Created by zhao on 2017/7/26. |
* @author fengyue |
||||||
|
* @date 2020/10/2 10:08 |
||||||
*/ |
*/ |
||||||
|
public class SearchBookAdapter extends BaseListAdapter<SearchBookBean> { |
||||||
public class SearchBookAdapter extends DragAdapter { |
|
||||||
|
|
||||||
private int mResourceId; |
|
||||||
|
|
||||||
private Context mContext; |
|
||||||
|
|
||||||
private ConcurrentMultiValueMap<SearchBookBean, Book> mBooks; |
private ConcurrentMultiValueMap<SearchBookBean, Book> mBooks; |
||||||
|
|
||||||
private ArrayList<SearchBookBean> mSearchBookBeans; |
public SearchBookAdapter(ConcurrentMultiValueMap<SearchBookBean, Book> mBooks) { |
||||||
|
|
||||||
private ArrayList<SearchBookBean> mTempSearchBookBeans = new ArrayList<>(); |
|
||||||
|
|
||||||
private HashMap<SearchBookBean, Book> mTempBooks = new HashMap<>(); |
|
||||||
|
|
||||||
private Handler mHandle = new Handler(message -> { |
|
||||||
|
|
||||||
switch (message.what) { |
|
||||||
case 1: |
|
||||||
ViewHolder holder = (ViewHolder) message.obj; |
|
||||||
int pos = message.arg1; |
|
||||||
SearchBookAdapter.this.initOtherInfo(pos, holder); |
|
||||||
break; |
|
||||||
} |
|
||||||
return false; |
|
||||||
}); |
|
||||||
|
|
||||||
public SearchBookAdapter(Context context, int resourceId, ArrayList<SearchBookBean> datas, |
|
||||||
ConcurrentMultiValueMap<SearchBookBean, Book> mBooks){ |
|
||||||
mContext = context; |
|
||||||
mSearchBookBeans = datas; |
|
||||||
mResourceId = resourceId; |
|
||||||
this.mBooks = mBooks; |
this.mBooks = mBooks; |
||||||
} |
} |
||||||
|
|
||||||
@Override |
@Override |
||||||
public int getCount() { |
protected IViewHolder<SearchBookBean> createViewHolder(int viewType) { |
||||||
return mSearchBookBeans.size(); |
return new SearchBookHolder(mBooks); |
||||||
|
} |
||||||
|
|
||||||
|
public synchronized void addAll(List<SearchBookBean> newDataS, String keyWord) { |
||||||
|
List<SearchBookBean> copyDataS = mList; |
||||||
|
if (newDataS != null && newDataS.size() > 0) { |
||||||
|
List<SearchBookBean> searchBookBeansAdd = new ArrayList<>(); |
||||||
|
if (copyDataS.size() == 0) { |
||||||
|
copyDataS.addAll(newDataS); |
||||||
|
} else { |
||||||
|
//存在
|
||||||
|
for (SearchBookBean temp : newDataS) { |
||||||
|
boolean hasSame = false; |
||||||
|
for (int i = 0, size = copyDataS.size(); i < size; i++) { |
||||||
|
SearchBookBean searchBook = copyDataS.get(i); |
||||||
|
if (TextUtils.equals(temp.getName(), searchBook.getName()) |
||||||
|
&& TextUtils.equals(temp.getAuthor(), searchBook.getAuthor())) { |
||||||
|
hasSame = true; |
||||||
|
break; |
||||||
} |
} |
||||||
|
|
||||||
@Override |
|
||||||
public SearchBookBean getItem(int position) { |
|
||||||
return mSearchBookBeans.get(position); |
|
||||||
} |
} |
||||||
|
|
||||||
@Override |
if (!hasSame) { |
||||||
public long getItemId(int position) { |
searchBookBeansAdd.add(temp); |
||||||
return 0; |
|
||||||
} |
} |
||||||
|
|
||||||
@NonNull |
|
||||||
@Override |
|
||||||
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { |
|
||||||
ViewHolder viewHolder = null; |
|
||||||
if (convertView == null){ |
|
||||||
viewHolder = new ViewHolder(); |
|
||||||
convertView = LayoutInflater.from(mContext).inflate(mResourceId,null); |
|
||||||
viewHolder.ivBookImg = convertView.findViewById(R.id.iv_book_img); |
|
||||||
viewHolder.tvBookName = convertView.findViewById(R.id.tv_book_name); |
|
||||||
viewHolder.tvAuthor = convertView.findViewById(R.id.tv_book_author); |
|
||||||
viewHolder.tvDesc = convertView.findViewById(R.id.tv_book_desc); |
|
||||||
viewHolder.tvType = convertView.findViewById(R.id.tv_book_type); |
|
||||||
viewHolder.tvSource = convertView.findViewById(R.id.tv_book_source); |
|
||||||
viewHolder.tvNewestChapter = convertView.findViewById(R.id.tv_book_newest_chapter); |
|
||||||
//viewHolder.tvNewestChapter = convertView.findViewById(R.id.tv_book_newest_chapter);
|
|
||||||
convertView.setTag(viewHolder); |
|
||||||
}else { |
|
||||||
viewHolder = (ViewHolder) convertView.getTag(); |
|
||||||
} |
|
||||||
initView(position,viewHolder); |
|
||||||
return convertView; |
|
||||||
} |
|
||||||
|
|
||||||
private void initView(final int position, final ViewHolder viewHolder){ |
|
||||||
List<Book> aBooks = mBooks.getValues(getItem(position)); |
|
||||||
int bookCount = aBooks.size(); |
|
||||||
Book book = aBooks.get(0); |
|
||||||
SearchBookBean ssb = new SearchBookBean(book.getName(), book.getAuthor()); |
|
||||||
|
|
||||||
//判断是否已经加载,防止多次加载
|
|
||||||
/*for (SearchBookBean temp : mTempSearchBookBeans){//已加载
|
|
||||||
if (ssb.equals(temp)){ |
|
||||||
book = mTempBooks.get(ssb); |
|
||||||
} |
} |
||||||
}*/ |
//添加
|
||||||
|
for (SearchBookBean temp : searchBookBeansAdd) { |
||||||
if (StringHelper.isEmpty(book.getImgUrl())){ |
if (TextUtils.equals(keyWord, temp.getName())) { |
||||||
book.setImgUrl(""); |
for (int i = 0; i < copyDataS.size(); i++) { |
||||||
|
SearchBookBean searchBook = copyDataS.get(i); |
||||||
|
if (!TextUtils.equals(keyWord, searchBook.getName())) { |
||||||
|
copyDataS.add(i, temp); |
||||||
|
break; |
||||||
} |
} |
||||||
viewHolder.tvBookName.setText(book.getName()); |
|
||||||
viewHolder.tvNewestChapter.setText("最新章节:" + book.getNewestChapterTitle()); |
|
||||||
viewHolder.tvAuthor.setText(book.getAuthor()); |
|
||||||
viewHolder.tvSource.setText("书源:" + BookSource.fromString(book.getSource()).text |
|
||||||
+ " 共" + bookCount + "个源"); |
|
||||||
viewHolder.tvDesc.setText(""); |
|
||||||
viewHolder.tvType.setText(""); |
|
||||||
ReadCrawler rc = ReadCrawlerUtil.getReadCrawler(book.getSource()); |
|
||||||
if (rc instanceof BookInfoCrawler && StringHelper.isEmpty(book.getImgUrl())){ |
|
||||||
BookInfoCrawler bic = (BookInfoCrawler) rc; |
|
||||||
CommonApi.getBookInfo(book, bic, new ResultCallback() { |
|
||||||
@Override |
|
||||||
public void onFinish(Object o, int code) { |
|
||||||
mHandle.sendMessage(mHandle.obtainMessage(1, position,0,viewHolder)); |
|
||||||
} |
} |
||||||
|
} else if (TextUtils.equals(keyWord, temp.getAuthor())) { |
||||||
@Override |
for (int i = 0; i < copyDataS.size(); i++) { |
||||||
public void onError(Exception e) { |
SearchBookBean searchBook = copyDataS.get(i); |
||||||
|
if (!TextUtils.equals(keyWord, searchBook.getName()) && !TextUtils.equals(keyWord, searchBook.getAuthor())) { |
||||||
|
copyDataS.add(i, temp); |
||||||
|
break; |
||||||
} |
} |
||||||
}); |
|
||||||
}else { |
|
||||||
initOtherInfo(position, viewHolder); |
|
||||||
} |
} |
||||||
//viewHolder.tvNewestChapter.setText(book.getNewestChapterTitle());
|
} else { |
||||||
//添加已经加载的书籍
|
copyDataS.add(temp); |
||||||
/*mTempSearchBookBeans.add(ssb); |
|
||||||
mTempBooks.put(ssb, book);*/ |
|
||||||
} |
} |
||||||
private void initOtherInfo(final int position, final ViewHolder holder){ |
|
||||||
Book book = mBooks.getValue(getItem(position), 0); |
|
||||||
//图片
|
|
||||||
if (!MyApplication.isDestroy((Activity) mContext)) { |
|
||||||
Glide.with(mContext) |
|
||||||
.load(book.getImgUrl()) |
|
||||||
// .override(DipPxUtil.dip2px(getContext(), 80), DipPxUtil.dip2px(getContext(), 150))
|
|
||||||
.error(R.mipmap.no_image) |
|
||||||
.placeholder(R.mipmap.no_image) |
|
||||||
//设置圆角
|
|
||||||
.apply(RequestOptions.bitmapTransform(new RoundedCorners(8))) |
|
||||||
.into(holder.ivBookImg); |
|
||||||
} |
} |
||||||
//简介
|
|
||||||
holder.tvDesc.setText("简介:" + book.getDesc()); |
|
||||||
holder.tvType.setText(book.getType()); |
|
||||||
} |
} |
||||||
|
MyApplication.runOnUiThread(this::notifyDataSetChanged); |
||||||
@Override |
|
||||||
public void onDataModelMove(int from, int to) { |
|
||||||
|
|
||||||
} |
} |
||||||
|
|
||||||
class ViewHolder{ |
|
||||||
ImageView ivBookImg; |
|
||||||
TextView tvBookName; |
|
||||||
TextView tvDesc; |
|
||||||
TextView tvAuthor; |
|
||||||
TextView tvType; |
|
||||||
TextView tvSource; |
|
||||||
TextView tvNewestChapter; |
|
||||||
//TextView tvNewestChapter;
|
|
||||||
} |
} |
||||||
|
|
||||||
} |
} |
||||||
|
@ -0,0 +1,19 @@ |
|||||||
|
package xyz.fycz.myreader.ui.adapter; |
||||||
|
|
||||||
|
import xyz.fycz.myreader.base.adapter.BaseListAdapter; |
||||||
|
import xyz.fycz.myreader.base.adapter.IViewHolder; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.ui.adapter.holder.SourceExchangeHolder; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2020/9/30 18:42 |
||||||
|
*/ |
||||||
|
public class SourceExchangeAdapter extends BaseListAdapter<Book> { |
||||||
|
@Override |
||||||
|
protected IViewHolder createViewHolder(int viewType) { |
||||||
|
return new SourceExchangeHolder(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,134 @@ |
|||||||
|
package xyz.fycz.myreader.ui.adapter.holder; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.os.Handler; |
||||||
|
import android.util.Log; |
||||||
|
import android.widget.ImageView; |
||||||
|
import android.widget.TextView; |
||||||
|
import com.bumptech.glide.Glide; |
||||||
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners; |
||||||
|
import com.bumptech.glide.request.RequestOptions; |
||||||
|
import xyz.fycz.myreader.R; |
||||||
|
import xyz.fycz.myreader.application.MyApplication; |
||||||
|
import xyz.fycz.myreader.base.adapter.ViewHolderImpl; |
||||||
|
import xyz.fycz.myreader.webapi.callback.ResultCallback; |
||||||
|
import xyz.fycz.myreader.entity.SearchBookBean; |
||||||
|
import xyz.fycz.myreader.enums.BookSource; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; |
||||||
|
import xyz.fycz.myreader.util.StringHelper; |
||||||
|
import xyz.fycz.myreader.webapi.CommonApi; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2020/10/2 10:10 |
||||||
|
*/ |
||||||
|
public class SearchBookHolder extends ViewHolderImpl<SearchBookBean> { |
||||||
|
private ConcurrentMultiValueMap<SearchBookBean, Book> mBooks; |
||||||
|
private Handler mHandle = new Handler(message -> { |
||||||
|
|
||||||
|
switch (message.what) { |
||||||
|
case 1: |
||||||
|
Book book = (Book) message.obj; |
||||||
|
initOtherInfo(book); |
||||||
|
break; |
||||||
|
} |
||||||
|
return false; |
||||||
|
}); |
||||||
|
|
||||||
|
public SearchBookHolder(ConcurrentMultiValueMap<SearchBookBean, Book> mBooks) { |
||||||
|
this.mBooks = mBooks; |
||||||
|
} |
||||||
|
|
||||||
|
ImageView ivBookImg; |
||||||
|
TextView tvBookName; |
||||||
|
TextView tvDesc; |
||||||
|
TextView tvAuthor; |
||||||
|
TextView tvType; |
||||||
|
TextView tvSource; |
||||||
|
TextView tvNewestChapter; |
||||||
|
@Override |
||||||
|
protected int getItemLayoutId() { |
||||||
|
return R.layout.listview_search_book_item; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void initView() { |
||||||
|
ivBookImg = findById(R.id.iv_book_img); |
||||||
|
tvBookName = findById(R.id.tv_book_name); |
||||||
|
tvAuthor = findById(R.id.tv_book_author); |
||||||
|
tvDesc = findById(R.id.tv_book_desc); |
||||||
|
tvType = findById(R.id.tv_book_type); |
||||||
|
tvSource = findById(R.id.tv_book_source); |
||||||
|
tvNewestChapter = findById(R.id.tv_book_newest_chapter); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void onBind(SearchBookBean data, int pos) { |
||||||
|
List<Book> aBooks = mBooks.getValues(data); |
||||||
|
int bookCount = aBooks.size(); |
||||||
|
Book book = aBooks.get(0); |
||||||
|
if (StringHelper.isEmpty(book.getImgUrl())){ |
||||||
|
book.setImgUrl(""); |
||||||
|
} |
||||||
|
tvBookName.setText(book.getName()); |
||||||
|
tvNewestChapter.setText(getContext().getString(R.string.newest_chapter, book.getNewestChapterTitle())); |
||||||
|
tvAuthor.setText(book.getAuthor()); |
||||||
|
tvSource.setText(getContext().getString(R.string.source_title_num, BookSource.fromString(book.getSource()).text, bookCount)); |
||||||
|
tvDesc.setText(""); |
||||||
|
tvType.setText(""); |
||||||
|
ReadCrawler rc = ReadCrawlerUtil.getReadCrawler(book.getSource()); |
||||||
|
if (rc instanceof BookInfoCrawler){ |
||||||
|
if (tvBookName.getTag() == null || !(Boolean) tvBookName.getTag()) { |
||||||
|
tvBookName.setTag(true); |
||||||
|
} else { |
||||||
|
initOtherInfo(book); |
||||||
|
return; |
||||||
|
} |
||||||
|
Log.i(book.getName(), "initOtherInfo"); |
||||||
|
BookInfoCrawler bic = (BookInfoCrawler) rc; |
||||||
|
CommonApi.getBookInfo(book, bic, new ResultCallback() { |
||||||
|
@Override |
||||||
|
public void onFinish(Object o, int code) { |
||||||
|
mHandle.sendMessage(mHandle.obtainMessage(1, pos, 0, book)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onError(Exception e) { |
||||||
|
tvBookName.setTag(false); |
||||||
|
} |
||||||
|
}); |
||||||
|
}else { |
||||||
|
initOtherInfo(book); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void initOtherInfo(Book book){ |
||||||
|
//图片
|
||||||
|
if (!MyApplication.isDestroy((Activity) getContext())) { |
||||||
|
Glide.with(getContext()) |
||||||
|
.load(book.getImgUrl()) |
||||||
|
// .override(DipPxUtil.dip2px(getContext(), 80), DipPxUtil.dip2px(getContext(), 150))
|
||||||
|
.error(R.mipmap.no_image) |
||||||
|
.placeholder(R.mipmap.no_image) |
||||||
|
//设置圆角
|
||||||
|
.apply(RequestOptions.bitmapTransform(new RoundedCorners(8))) |
||||||
|
.into(ivBookImg); |
||||||
|
} |
||||||
|
//简介
|
||||||
|
if (book.getDesc() == null) { |
||||||
|
tvDesc.setText(""); |
||||||
|
}else { |
||||||
|
tvDesc.setText("简介:" + book.getDesc()); |
||||||
|
} |
||||||
|
tvType.setText(book.getType()); |
||||||
|
tvNewestChapter.setText("最新章节:" + book.getNewestChapterTitle()); |
||||||
|
tvAuthor.setText(book.getAuthor()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
package xyz.fycz.myreader.ui.adapter.holder; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
import android.widget.ImageView; |
||||||
|
import android.widget.TextView; |
||||||
|
import xyz.fycz.myreader.R; |
||||||
|
import xyz.fycz.myreader.base.adapter.ViewHolderImpl; |
||||||
|
import xyz.fycz.myreader.enums.BookSource; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2020/9/30 18:43 |
||||||
|
*/ |
||||||
|
public class SourceExchangeHolder extends ViewHolderImpl<Book> { |
||||||
|
TextView sourceTvTitle; |
||||||
|
TextView sourceTvChapter; |
||||||
|
ImageView sourceIv; |
||||||
|
@Override |
||||||
|
protected int getItemLayoutId() { |
||||||
|
return R.layout.item_change_source; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void initView() { |
||||||
|
sourceTvTitle = findById(R.id.tv_source_name); |
||||||
|
sourceTvChapter = findById(R.id.tv_lastChapter); |
||||||
|
sourceIv = findById(R.id.iv_checked); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onBind(Book data, int pos) { |
||||||
|
sourceTvTitle.setText(BookSource.fromString(data.getSource()).text); |
||||||
|
sourceTvChapter.setText(data.getNewestChapterTitle()); |
||||||
|
if (Boolean.parseBoolean(data.getNewestChapterId())) |
||||||
|
sourceIv.setVisibility(View.VISIBLE); |
||||||
|
else |
||||||
|
sourceIv.setVisibility(View.GONE); |
||||||
|
} |
||||||
|
} |
@ -1,17 +1,19 @@ |
|||||||
package xyz.fycz.myreader.creator; |
package xyz.fycz.myreader.ui.dialog; |
||||||
|
|
||||||
import android.annotation.SuppressLint; |
import android.annotation.SuppressLint; |
||||||
import android.content.Context; |
import android.content.Context; |
||||||
import android.os.Handler; |
import android.os.Handler; |
||||||
import android.os.Message; |
import android.os.Message; |
||||||
import xyz.fycz.myreader.application.MyApplication; |
import xyz.fycz.myreader.application.MyApplication; |
||||||
import xyz.fycz.myreader.callback.ResultCallback; |
import xyz.fycz.myreader.webapi.callback.ResultCallback; |
||||||
import xyz.fycz.myreader.util.ToastUtils; |
import xyz.fycz.myreader.util.ToastUtils; |
||||||
import xyz.fycz.myreader.webapi.crawler.*; |
import xyz.fycz.myreader.webapi.crawler.*; |
||||||
import xyz.fycz.myreader.entity.SearchBookBean; |
import xyz.fycz.myreader.entity.SearchBookBean; |
||||||
import xyz.fycz.myreader.greendao.entity.Book; |
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap; |
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; |
||||||
import xyz.fycz.myreader.webapi.CommonApi; |
import xyz.fycz.myreader.webapi.CommonApi; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; |
||||||
|
|
||||||
import java.io.UnsupportedEncodingException; |
import java.io.UnsupportedEncodingException; |
||||||
import java.net.URLEncoder; |
import java.net.URLEncoder; |
@ -1,4 +1,4 @@ |
|||||||
package xyz.fycz.myreader.creator; |
package xyz.fycz.myreader.ui.dialog; |
||||||
|
|
||||||
import android.content.Context; |
import android.content.Context; |
||||||
import androidx.appcompat.app.AlertDialog; |
import androidx.appcompat.app.AlertDialog; |
@ -0,0 +1,222 @@ |
|||||||
|
package xyz.fycz.myreader.ui.dialog; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.app.Dialog; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.view.Gravity; |
||||||
|
import android.view.View; |
||||||
|
import android.view.Window; |
||||||
|
import android.view.WindowManager; |
||||||
|
import android.widget.TextView; |
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.appcompat.app.AlertDialog; |
||||||
|
import androidx.appcompat.widget.AppCompatImageView; |
||||||
|
import androidx.appcompat.widget.Toolbar; |
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager; |
||||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||||
|
import butterknife.BindView; |
||||||
|
import butterknife.ButterKnife; |
||||||
|
import xyz.fycz.myreader.R; |
||||||
|
import xyz.fycz.myreader.entity.SearchBookBean; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.model.SearchEngine; |
||||||
|
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; |
||||||
|
import xyz.fycz.myreader.ui.adapter.SourceExchangeAdapter; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil; |
||||||
|
import xyz.fycz.myreader.widget.RefreshProgressBar; |
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Zhouas666 on 2019-04-11 |
||||||
|
* Github: https://github.com/zas023
|
||||||
|
* <p> |
||||||
|
* 换源dialog |
||||||
|
*/ |
||||||
|
|
||||||
|
public class SourceExchangeDialog extends Dialog { |
||||||
|
|
||||||
|
private static final String TAG = "SourceExchangeDialog"; |
||||||
|
/*@BindView(R.id.dialog_tv_title) |
||||||
|
TextView dialogTvTitle;*/ |
||||||
|
@BindView(R.id.toolbar) |
||||||
|
Toolbar toolbar; |
||||||
|
@BindView(R.id.iv_refresh_search) |
||||||
|
AppCompatImageView ivRefreshSearch; |
||||||
|
@BindView(R.id.iv_stop_search) |
||||||
|
AppCompatImageView ivStopSearch; |
||||||
|
@BindView(R.id.rpb) |
||||||
|
RefreshProgressBar rpb; |
||||||
|
@BindView(R.id.dialog_rv_content) |
||||||
|
RecyclerView dialogRvContent; |
||||||
|
|
||||||
|
private SearchEngine searchEngine; |
||||||
|
private SourceExchangeAdapter mAdapter; |
||||||
|
|
||||||
|
private OnSourceChangeListener listener; |
||||||
|
|
||||||
|
private Activity mActivity; |
||||||
|
private Book mShelfBook; |
||||||
|
private List<Book> aBooks; |
||||||
|
|
||||||
|
private AlertDialog mErrorDia; |
||||||
|
|
||||||
|
private int sourceIndex = -1; |
||||||
|
|
||||||
|
/***************************************************************************/ |
||||||
|
public SourceExchangeDialog(@NonNull Activity activity, Book bookBean) { |
||||||
|
super(activity); |
||||||
|
mActivity = activity; |
||||||
|
mShelfBook = bookBean; |
||||||
|
} |
||||||
|
|
||||||
|
public void setShelfBook(Book mShelfBook) { |
||||||
|
this.mShelfBook = mShelfBook; |
||||||
|
} |
||||||
|
|
||||||
|
public void setABooks(List<Book> aBooks){ |
||||||
|
this.aBooks = aBooks; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSourceIndex(int sourceIndex){ |
||||||
|
this.sourceIndex = sourceIndex; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOnSourceChangeListener(OnSourceChangeListener listener) { |
||||||
|
this.listener = listener; |
||||||
|
} |
||||||
|
|
||||||
|
public List<Book> getaBooks(){return aBooks;} |
||||||
|
/*****************************Initialization********************************/ |
||||||
|
@Override |
||||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||||
|
super.onCreate(savedInstanceState); |
||||||
|
setContentView(R.layout.dialog_book_source); |
||||||
|
ButterKnife.bind(this); |
||||||
|
setUpWindow(); |
||||||
|
initData(); |
||||||
|
initClick(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onStart() { |
||||||
|
super.onStart(); |
||||||
|
//执行业务逻辑
|
||||||
|
if (aBooks.size() == 0) { |
||||||
|
searchEngine.search(mShelfBook.getName(), mShelfBook.getAuthor()); |
||||||
|
ivStopSearch.setVisibility(View.VISIBLE); |
||||||
|
rpb.setIsAutoLoading(true); |
||||||
|
}else { |
||||||
|
if (mAdapter.getItemCount() == 0) { |
||||||
|
mAdapter.addItems(aBooks); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置Dialog显示的位置 |
||||||
|
*/ |
||||||
|
private void setUpWindow() { |
||||||
|
Window window = getWindow(); |
||||||
|
WindowManager.LayoutParams lp = window.getAttributes(); |
||||||
|
lp.width = WindowManager.LayoutParams.MATCH_PARENT; |
||||||
|
lp.height = WindowManager.LayoutParams.WRAP_CONTENT; |
||||||
|
lp.gravity = Gravity.CENTER; |
||||||
|
window.setAttributes(lp); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 初始化数据 |
||||||
|
*/ |
||||||
|
private void initData() { |
||||||
|
toolbar.setTitle(mShelfBook.getName()); |
||||||
|
toolbar.setSubtitle(mShelfBook.getAuthor()); |
||||||
|
//dialogTvTitle.setText(mShelfBook.getName() + "(" + mShelfBook.getAuthor() + ")");
|
||||||
|
|
||||||
|
if (aBooks == null) { |
||||||
|
aBooks = new ArrayList<>(); |
||||||
|
} |
||||||
|
|
||||||
|
mAdapter = new SourceExchangeAdapter(); |
||||||
|
dialogRvContent.setLayoutManager(new LinearLayoutManager(mActivity)); |
||||||
|
dialogRvContent.setAdapter(mAdapter); |
||||||
|
|
||||||
|
searchEngine = new SearchEngine(); |
||||||
|
searchEngine.initSearchEngine(ReadCrawlerUtil.getReadCrawlers()); |
||||||
|
} |
||||||
|
|
||||||
|
private void initClick() { |
||||||
|
searchEngine.setOnSearchListener(new SearchEngine.OnSearchListener() { |
||||||
|
@Override |
||||||
|
public void loadMoreFinish(Boolean isAll) { |
||||||
|
synchronized (RefreshProgressBar.class) { |
||||||
|
rpb.setIsAutoLoading(false); |
||||||
|
ivStopSearch.setVisibility(View.GONE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void loadMoreSearchBook(ConcurrentMultiValueMap<SearchBookBean, Book> items) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void loadMoreSearchBook(List<Book> items) { |
||||||
|
//确保只有一个结果
|
||||||
|
if (items != null && items.size() != 0) { |
||||||
|
Book bean = items.get(0); |
||||||
|
if (bean.getSource().equals(mShelfBook.getSource())) { |
||||||
|
bean.setNewestChapterId("true"); |
||||||
|
sourceIndex = mAdapter.getItemSize(); |
||||||
|
} |
||||||
|
mAdapter.addItem(items.get(0)); |
||||||
|
aBooks.add(bean); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void searchBookError(Throwable throwable) { |
||||||
|
dismiss(); |
||||||
|
DialogCreator.createTipDialog(mActivity, "未搜索到该书籍,书源加载失败!"); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
mAdapter.setOnItemClickListener((view, pos) -> { |
||||||
|
if (listener == null) return; |
||||||
|
Book newBook = mAdapter.getItem(pos); |
||||||
|
if (mShelfBook.getSource() == null) { |
||||||
|
listener.onSourceChanged(newBook, pos); |
||||||
|
searchEngine.stopSearch(); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (mShelfBook.getSource().equals(newBook.getSource())) return; |
||||||
|
mShelfBook = newBook; |
||||||
|
listener.onSourceChanged(newBook, pos); |
||||||
|
mAdapter.getItem(pos).setNewestChapterId("true"); |
||||||
|
if (sourceIndex > -1) |
||||||
|
mAdapter.getItem(sourceIndex).setNewestChapterId("false"); |
||||||
|
sourceIndex = pos; |
||||||
|
mAdapter.notifyDataSetChanged(); |
||||||
|
dismiss(); |
||||||
|
}); |
||||||
|
|
||||||
|
ivStopSearch.setOnClickListener(v -> searchEngine.stopSearch()); |
||||||
|
ivRefreshSearch.setOnClickListener(v -> { |
||||||
|
searchEngine.stopSearch(); |
||||||
|
ivStopSearch.setVisibility(View.VISIBLE); |
||||||
|
mAdapter.clear(); |
||||||
|
aBooks.clear(); |
||||||
|
mAdapter.notifyDataSetChanged(); |
||||||
|
searchEngine.search(mShelfBook.getName(), mShelfBook.getAuthor()); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/**************************Interface**********************************/ |
||||||
|
public interface OnSourceChangeListener { |
||||||
|
void onSourceChanged(Book bean, int pos); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,20 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
|
|
||||||
import java.util.regex.Matcher; |
|
||||||
import java.util.regex.Pattern; |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class EmailUtils { |
|
||||||
/** |
|
||||||
* 检验邮箱格式 |
|
||||||
* @param email |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static boolean isMatched(String email){ |
|
||||||
String check = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; |
|
||||||
Pattern regex = Pattern.compile(check); |
|
||||||
Matcher matcher = regex.matcher(email); |
|
||||||
return matcher.matches(); |
|
||||||
} |
|
||||||
} |
|
@ -1,58 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
|
|
||||||
import java.text.ParseException; |
|
||||||
import java.text.SimpleDateFormat; |
|
||||||
import java.util.Date; |
|
||||||
import java.util.GregorianCalendar; |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class FormatDateUtils { |
|
||||||
private static Date date; |
|
||||||
private static SimpleDateFormat format; |
|
||||||
|
|
||||||
private static SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); |
|
||||||
|
|
||||||
public static String formatDateTime(long time) { |
|
||||||
if (0 == time) { |
|
||||||
return ""; |
|
||||||
} |
|
||||||
return mDateFormat.format(new Date(time)); |
|
||||||
} |
|
||||||
|
|
||||||
public static String long2date(String str, String formater){ |
|
||||||
return formateLongTime(long2String(str),formater); |
|
||||||
} |
|
||||||
|
|
||||||
public static String long2date(long times, String formater){ |
|
||||||
return formateLongTime(times,formater); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
private static String formateLongTime(long times, String formater){ |
|
||||||
date=new Date(times); |
|
||||||
GregorianCalendar gc = new GregorianCalendar(); |
|
||||||
gc.setTime(date); |
|
||||||
format = new SimpleDateFormat(formater); |
|
||||||
String sb=format.format(gc.getTime()); |
|
||||||
return sb; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static long long2String(String str){ |
|
||||||
long time = 0; |
|
||||||
try { |
|
||||||
time = mDateFormat.parse(str).getTime(); |
|
||||||
} catch (ParseException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
return time; |
|
||||||
} |
|
||||||
|
|
||||||
public static long day2long(long times){ |
|
||||||
long day = times / (1000*60*60*24); |
|
||||||
return day; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,272 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import android.graphics.*; |
|
||||||
import android.graphics.drawable.BitmapDrawable; |
|
||||||
import android.graphics.drawable.Drawable; |
|
||||||
import xyz.fycz.myreader.R; |
|
||||||
import xyz.fycz.myreader.application.MyApplication; |
|
||||||
import xyz.fycz.myreader.callback.ResultCallback; |
|
||||||
|
|
||||||
import static xyz.fycz.myreader.util.DipPxUtil.dp2px; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author fengyue |
|
||||||
* @date 2020/8/13 7:56 |
|
||||||
*/ |
|
||||||
public class ImageUtil { |
|
||||||
/** |
|
||||||
* 设置水印图片在左上角 |
|
||||||
* @param context |
|
||||||
* @param src |
|
||||||
* @param watermark |
|
||||||
* @param paddingLeft |
|
||||||
* @param paddingTop |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static Bitmap createWaterMaskLeftTop( |
|
||||||
Context context, Bitmap src, Bitmap watermark, |
|
||||||
int paddingLeft, int paddingTop) { |
|
||||||
return createWaterMaskBitmap(src, watermark, |
|
||||||
dp2px(context, paddingLeft), dp2px(context, paddingTop)); |
|
||||||
} |
|
||||||
|
|
||||||
private static Bitmap createWaterMaskBitmap(Bitmap src, Bitmap watermark, |
|
||||||
int paddingLeft, int paddingTop) { |
|
||||||
if (src == null) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
int width = src.getWidth(); |
|
||||||
int height = src.getHeight(); |
|
||||||
//创建一个bitmap
|
|
||||||
Bitmap newb = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图
|
|
||||||
//将该图片作为画布
|
|
||||||
Canvas canvas = new Canvas(newb); |
|
||||||
//在画布 0,0坐标上开始绘制原始图片
|
|
||||||
canvas.drawBitmap(src, 0, 0, null); |
|
||||||
//在画布上绘制水印图片
|
|
||||||
canvas.drawBitmap(watermark, paddingLeft, paddingTop, null); |
|
||||||
// 保存
|
|
||||||
canvas.save(); |
|
||||||
// 存储
|
|
||||||
canvas.restore(); |
|
||||||
return newb; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置水印图片在右下角 |
|
||||||
* @param context |
|
||||||
* @param src |
|
||||||
* @param watermark |
|
||||||
* @param paddingRight |
|
||||||
* @param paddingBottom |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static Bitmap createWaterMaskRightBottom( |
|
||||||
Context context, Bitmap src, Bitmap watermark, |
|
||||||
int paddingRight, int paddingBottom) { |
|
||||||
return createWaterMaskBitmap(src, watermark, |
|
||||||
src.getWidth() - watermark.getWidth() - dp2px(context, paddingRight), |
|
||||||
src.getHeight() - watermark.getHeight() - dp2px(context, paddingBottom)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置水印图片到右上角 |
|
||||||
* @param context |
|
||||||
* @param src |
|
||||||
* @param watermark |
|
||||||
* @param paddingRight |
|
||||||
* @param paddingTop |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static Bitmap createWaterMaskRightTop( |
|
||||||
Context context, Bitmap src, Bitmap watermark, |
|
||||||
int paddingRight, int paddingTop) { |
|
||||||
return createWaterMaskBitmap( src, watermark, |
|
||||||
src.getWidth() - watermark.getWidth() - dp2px(context, paddingRight), |
|
||||||
dp2px(context, paddingTop)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置水印图片到左下角 |
|
||||||
* @param context |
|
||||||
* @param src |
|
||||||
* @param watermark |
|
||||||
* @param paddingLeft |
|
||||||
* @param paddingBottom |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static Bitmap createWaterMaskLeftBottom( |
|
||||||
Context context, Bitmap src, Bitmap watermark, |
|
||||||
int paddingLeft, int paddingBottom) { |
|
||||||
return createWaterMaskBitmap(src, watermark, dp2px(context, paddingLeft), |
|
||||||
src.getHeight() - watermark.getHeight() - dp2px(context, paddingBottom)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置水印图片到中间 |
|
||||||
* @param src |
|
||||||
* @param src |
|
||||||
* @param watermark |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static Bitmap createWaterMaskCenter(Bitmap src, Bitmap watermark) { |
|
||||||
return createWaterMaskBitmap(src, watermark, |
|
||||||
(src.getWidth() - watermark.getWidth()) / 2, |
|
||||||
(src.getHeight() - watermark.getHeight()) / 2); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 给图片添加文字到左上角 |
|
||||||
* @param context |
|
||||||
* @param bitmap |
|
||||||
* @param text |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static Bitmap drawTextToLeftTop(Context context, Bitmap bitmap, String text, |
|
||||||
int size, int color, int paddingLeft, int paddingTop) { |
|
||||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); |
|
||||||
paint.setColor(color); |
|
||||||
paint.setTextSize(dp2px(context, size)); |
|
||||||
Rect bounds = new Rect(); |
|
||||||
paint.getTextBounds(text, 0, text.length(), bounds); |
|
||||||
return drawTextToBitmap(context, bitmap, text, paint, bounds, |
|
||||||
dp2px(context, paddingLeft), |
|
||||||
dp2px(context, paddingTop) + bounds.height()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 绘制文字到右下角 |
|
||||||
* @param context |
|
||||||
* @param bitmap |
|
||||||
* @param text |
|
||||||
* @param size |
|
||||||
* @param color |
|
||||||
* @param paddingRight |
|
||||||
* @param paddingBottom |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static Bitmap drawTextToRightBottom(Context context, Bitmap bitmap, String text, |
|
||||||
int size, int color, int paddingRight, int paddingBottom) { |
|
||||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); |
|
||||||
paint.setColor(color); |
|
||||||
paint.setTextSize(dp2px(context, size)); |
|
||||||
Rect bounds = new Rect(); |
|
||||||
paint.getTextBounds(text, 0, text.length(), bounds); |
|
||||||
return drawTextToBitmap(context, bitmap, text, paint, bounds, |
|
||||||
bitmap.getWidth() - bounds.width() - dp2px(context, paddingRight), |
|
||||||
bitmap.getHeight() - dp2px(context, paddingBottom)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 绘制文字到右上方 |
|
||||||
* @param context |
|
||||||
* @param bitmap |
|
||||||
* @param text |
|
||||||
* @param size |
|
||||||
* @param color |
|
||||||
* @param paddingRight |
|
||||||
* @param paddingTop |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static Bitmap drawTextToRightTop(Context context, Bitmap bitmap, String text, |
|
||||||
int size, int color, int paddingRight, int paddingTop) { |
|
||||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); |
|
||||||
paint.setColor(color); |
|
||||||
paint.setTextSize(dp2px(context, size)); |
|
||||||
Rect bounds = new Rect(); |
|
||||||
paint.getTextBounds(text, 0, text.length(), bounds); |
|
||||||
return drawTextToBitmap(context, bitmap, text, paint, bounds, |
|
||||||
bitmap.getWidth() - bounds.width() - dp2px(context, paddingRight), |
|
||||||
dp2px(context, paddingTop) + bounds.height()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 绘制文字到左下方 |
|
||||||
* @param context |
|
||||||
* @param bitmap |
|
||||||
* @param text |
|
||||||
* @param size |
|
||||||
* @param color |
|
||||||
* @param paddingLeft |
|
||||||
* @param paddingBottom |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static Bitmap drawTextToLeftBottom(Context context, Bitmap bitmap, String text, |
|
||||||
int size, int color, int paddingLeft, int paddingBottom) { |
|
||||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); |
|
||||||
paint.setColor(color); |
|
||||||
paint.setTextSize(dp2px(context, size)); |
|
||||||
Rect bounds = new Rect(); |
|
||||||
paint.getTextBounds(text, 0, text.length(), bounds); |
|
||||||
return drawTextToBitmap(context, bitmap, text, paint, bounds, |
|
||||||
dp2px(context, paddingLeft), |
|
||||||
bitmap.getHeight() - dp2px(context, paddingBottom)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 绘制文字到中间 |
|
||||||
* @param context |
|
||||||
* @param bitmap |
|
||||||
* @param text |
|
||||||
* @param size |
|
||||||
* @param color |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static Bitmap drawTextToCenter(Context context, Bitmap bitmap, String text, |
|
||||||
int size, int color) { |
|
||||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); |
|
||||||
paint.setColor(color); |
|
||||||
paint.setTextSize(dp2px(context, size)); |
|
||||||
Rect bounds = new Rect(); |
|
||||||
paint.getTextBounds(text, 0, text.length(), bounds); |
|
||||||
return drawTextToBitmap(context, bitmap, text, paint, bounds, |
|
||||||
(bitmap.getWidth() - bounds.width()) / 2, |
|
||||||
(bitmap.getHeight() + bounds.height()) / 2); |
|
||||||
} |
|
||||||
|
|
||||||
//图片上绘制文字
|
|
||||||
private static Bitmap drawTextToBitmap(Context context, Bitmap bitmap, String text, |
|
||||||
Paint paint, Rect bounds, int paddingLeft, int paddingTop) { |
|
||||||
android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig(); |
|
||||||
|
|
||||||
paint.setDither(true); // 获取跟清晰的图像采样
|
|
||||||
paint.setFilterBitmap(true);// 过滤一些
|
|
||||||
// paint.setFakeBoldText(true); //加粗
|
|
||||||
if (bitmapConfig == null) { |
|
||||||
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888; |
|
||||||
} |
|
||||||
|
|
||||||
bitmap = bitmap.copy(bitmapConfig, true); |
|
||||||
Canvas canvas = new Canvas(bitmap); |
|
||||||
canvas.drawText(text, paddingLeft, paddingTop, paint); |
|
||||||
return bitmap; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 缩放图片 |
|
||||||
* @param src |
|
||||||
* @param w |
|
||||||
* @param h |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static Bitmap scaleWithWH(Bitmap src, double w, double h) { |
|
||||||
if (w == 0 || h == 0 || src == null) { |
|
||||||
return src; |
|
||||||
} else { |
|
||||||
// 记录src的宽高
|
|
||||||
int width = src.getWidth(); |
|
||||||
int height = src.getHeight(); |
|
||||||
// 创建一个matrix容器
|
|
||||||
Matrix matrix = new Matrix(); |
|
||||||
// 计算缩放比例
|
|
||||||
float scaleWidth = (float) (w / width); |
|
||||||
float scaleHeight = (float) (h / height); |
|
||||||
// 开始缩放
|
|
||||||
matrix.postScale(scaleWidth, scaleHeight); |
|
||||||
// 创建缩放后的图片
|
|
||||||
return Bitmap.createBitmap(src, 0, 0, width, height, matrix, true); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,157 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
|
|
||||||
import android.os.Handler; |
|
||||||
import android.os.HandlerThread; |
|
||||||
import android.os.Message; |
|
||||||
import android.view.View; |
|
||||||
import android.view.ViewGroup; |
|
||||||
import android.widget.GridView; |
|
||||||
import android.widget.ListAdapter; |
|
||||||
import android.widget.ListView; |
|
||||||
|
|
||||||
import xyz.fycz.myreader.base.BaseActivity; |
|
||||||
|
|
||||||
import java.lang.reflect.Field; |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class ListViewHeight { |
|
||||||
private static HandlerThread thread = new HandlerThread("listview"); |
|
||||||
private static int totalHeight = 0; |
|
||||||
static { |
|
||||||
thread.start(); |
|
||||||
} |
|
||||||
private static Handler handler = new Handler(thread.getLooper()){ |
|
||||||
@Override |
|
||||||
public void handleMessage(Message msg) { |
|
||||||
switch (msg.what){ |
|
||||||
case 1: |
|
||||||
|
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
/** |
|
||||||
* 计算listview的高度 |
|
||||||
* @param listView |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static int setListViewHeightBasedOnChildren(final ListView listView) { |
|
||||||
ListAdapter listAdapter = listView.getAdapter(); |
|
||||||
if (listAdapter == null) { |
|
||||||
// pre-condition
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
int totalHeight = 0; |
|
||||||
for (int i = 0; i < listAdapter.getCount(); i++) { |
|
||||||
View listItem = listAdapter.getView(i, null, listView); |
|
||||||
listItem.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), |
|
||||||
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); |
|
||||||
// listItem.measure(0, 0);
|
|
||||||
totalHeight += listItem.getMeasuredHeight(); |
|
||||||
} |
|
||||||
final ViewGroup.LayoutParams params = listView.getLayoutParams(); |
|
||||||
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); |
|
||||||
// MyApplication.getApplication().runOnUiThread(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public void run() {
|
|
||||||
listView.setLayoutParams(params); |
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
return totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* 日期gridview的高度 |
|
||||||
* @param gridView |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static int setDateGridViewHeightBasedOnChildren(GridView gridView) { |
|
||||||
ListAdapter listAdapter = gridView.getAdapter(); |
|
||||||
if (listAdapter == null) { |
|
||||||
// pre-condition
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
int totalHeight = 0; |
|
||||||
for (int i = 0; i < (listAdapter.getCount()/7); i++) { |
|
||||||
View listItem = listAdapter.getView(i, null, gridView); |
|
||||||
listItem.measure(0, 0); |
|
||||||
totalHeight += listItem.getMeasuredHeight(); |
|
||||||
} |
|
||||||
ViewGroup.LayoutParams params = gridView.getLayoutParams(); |
|
||||||
params.height = totalHeight + (1 * (listAdapter.getCount()/7 - 1)); |
|
||||||
gridView.setLayoutParams(params); |
|
||||||
return totalHeight + (1 * (listAdapter.getCount()/7 - 1)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 计算gridview的高度 |
|
||||||
* @param gridView |
|
||||||
*/ |
|
||||||
public static void setGridViewHeightBasedOnChildren(GridView gridView) { |
|
||||||
// 获取GridView对应的Adapter
|
|
||||||
ListAdapter listAdapter = gridView.getAdapter(); |
|
||||||
if (listAdapter == null) { |
|
||||||
return; |
|
||||||
} |
|
||||||
int rows; |
|
||||||
int columnWidths; |
|
||||||
int columns=0; |
|
||||||
int horizontalBorderHeight = 0; |
|
||||||
int verticalBorderHeight = 0; |
|
||||||
|
|
||||||
Class<?> clazz=gridView.getClass(); |
|
||||||
try { |
|
||||||
//利用反射,取得横向分割线高度
|
|
||||||
Field vertical=clazz.getDeclaredField("mVerticalSpacing"); |
|
||||||
vertical.setAccessible(true); |
|
||||||
verticalBorderHeight =(Integer)vertical.get(gridView); |
|
||||||
|
|
||||||
//利用反射,取得纵向分割线高度
|
|
||||||
Field horizontalSpacing=clazz.getDeclaredField("mRequestedHorizontalSpacing"); |
|
||||||
horizontalSpacing.setAccessible(true); |
|
||||||
horizontalBorderHeight=(Integer)horizontalSpacing.get(gridView); |
|
||||||
|
|
||||||
//利用反射,取得每行显示的个数
|
|
||||||
Field column=clazz.getDeclaredField("mRequestedNumColumns"); |
|
||||||
column.setAccessible(true); |
|
||||||
columns=(Integer)column.get(gridView); |
|
||||||
|
|
||||||
if(columns == -1 || columns == 0){ |
|
||||||
Field columnWidth = clazz.getDeclaredField("mRequestedColumnWidth"); |
|
||||||
columnWidth.setAccessible(true); |
|
||||||
columnWidths = (Integer)columnWidth.get(gridView); |
|
||||||
int pad = gridView.getPaddingLeft() * 2; |
|
||||||
int width = BaseActivity.width - pad * 2; |
|
||||||
columns = width / (columnWidths /*+ horizontalBorderHeight*/); |
|
||||||
if(horizontalBorderHeight * (columns - 1) + columnWidths * columns > width){ |
|
||||||
columns --; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} catch (Exception e) { |
|
||||||
// TODO: handle exception
|
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
//判断数据总数除以每行个数是否整除。不能整除代表有多余,需要加一行
|
|
||||||
if(listAdapter.getCount()%columns > 0){ |
|
||||||
rows=listAdapter.getCount()/columns+1; |
|
||||||
}else { |
|
||||||
rows=listAdapter.getCount()/columns; |
|
||||||
} |
|
||||||
int totalHeight = 0; |
|
||||||
for (int i = 0; i < rows; i++) { //只计算每项高度*行数
|
|
||||||
View listItem = listAdapter.getView(i, null, gridView); |
|
||||||
listItem.measure(0, 0); // 计算子项View 的宽高
|
|
||||||
totalHeight += listItem.getMeasuredHeight(); // 统计所有子项的总高度
|
|
||||||
} |
|
||||||
ViewGroup.LayoutParams params = gridView.getLayoutParams(); |
|
||||||
params.height = totalHeight + gridView.getPaddingTop() + + gridView.getPaddingBottom() + verticalBorderHeight * (rows-1);//最后加上分割线总高度
|
|
||||||
gridView.setLayoutParams(params); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,69 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
|
|
||||||
import android.app.NotificationManager; |
|
||||||
import android.content.Context; |
|
||||||
|
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.HashMap; |
|
||||||
import java.util.Map; |
|
||||||
|
|
||||||
|
|
||||||
public class NotifyHelper { |
|
||||||
|
|
||||||
private static NotificationManager mNotificationManager; |
|
||||||
private static Map mNotifyId = new HashMap(); |
|
||||||
private static ArrayList<String> mCurrentConvIds = new ArrayList<String>(); |
|
||||||
|
|
||||||
public static void init(Context context){ |
|
||||||
mNotificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
public static int createNotifyId(String convId) { |
|
||||||
if (!mNotifyId.containsKey(convId)) { |
|
||||||
for (int i = 0; i < 1000; i++) { |
|
||||||
if (!mNotifyId.containsValue(i)) { |
|
||||||
mNotifyId.put(convId, i); |
|
||||||
return i; |
|
||||||
} |
|
||||||
} |
|
||||||
}else { |
|
||||||
return (int)mNotifyId.get(convId); |
|
||||||
} |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
public static void closeNotification(String convId){ |
|
||||||
//关闭通知
|
|
||||||
int notifyId = getNotifyId(convId); |
|
||||||
if(notifyId != -1){ |
|
||||||
mNotificationManager.cancel(notifyId); |
|
||||||
mNotifyId.remove(convId); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
public static int getNotifyId(String convId){ |
|
||||||
if(mNotifyId.get(convId) == null){ |
|
||||||
return -1; |
|
||||||
}else { |
|
||||||
return (int)mNotifyId.get(convId); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
public static boolean isOpenConv(String convId){ |
|
||||||
return mCurrentConvIds.contains(convId); |
|
||||||
} |
|
||||||
|
|
||||||
public static void addCurrentConv(String convId){ |
|
||||||
if(!mCurrentConvIds.contains(convId)){ |
|
||||||
mCurrentConvIds.add(convId); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static void removeCurrenConv(String convId){ |
|
||||||
mCurrentConvIds.remove(convId); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,149 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
|
|
||||||
import android.Manifest; |
|
||||||
import android.content.Context; |
|
||||||
import android.content.pm.PackageManager; |
|
||||||
import android.os.Build; |
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity; |
|
||||||
import androidx.core.app.ActivityCompat; |
|
||||||
import androidx.core.content.ContextCompat; |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class PermissionHelper { |
|
||||||
|
|
||||||
/** |
|
||||||
* 设备信息读取权限 |
|
||||||
* @param context |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static boolean isREAD_PHONE_STATE(Context context) { |
|
||||||
boolean permission = false; |
|
||||||
if (Build.VERSION.SDK_INT >= 23) { |
|
||||||
int checkReadPhoneStatePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE); |
|
||||||
if (checkReadPhoneStatePermission != PackageManager.PERMISSION_GRANTED) { |
|
||||||
// 弹出对话框接收权限
|
|
||||||
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.READ_PHONE_STATE}, 1); |
|
||||||
ToastUtils.showWarring("当前应用未拥读取设备状态权限"); |
|
||||||
} else if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { |
|
||||||
// 弹出对话框接收权限
|
|
||||||
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.READ_PHONE_STATE}, 1); |
|
||||||
ToastUtils.showWarring("当前应用未拥读取设备状态权限"); |
|
||||||
} else { |
|
||||||
permission = true; |
|
||||||
} |
|
||||||
} else { |
|
||||||
permission = true; |
|
||||||
} |
|
||||||
return permission; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 蓝牙设备权限 |
|
||||||
* @param context |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static boolean isACCESS_COARSE_LOCATION(Context context) { |
|
||||||
boolean permission = false; |
|
||||||
if (Build.VERSION.SDK_INT >= 23) { |
|
||||||
int checkReadPhoneStatePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION); |
|
||||||
if (checkReadPhoneStatePermission != PackageManager.PERMISSION_GRANTED) { |
|
||||||
// 弹出对话框接收权限
|
|
||||||
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION}, 1); |
|
||||||
ToastUtils.showWarring("当前应用未拥有蓝牙设备使用权限"); |
|
||||||
} else if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { |
|
||||||
// 弹出对话框接收权限
|
|
||||||
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION}, 1); |
|
||||||
ToastUtils.showWarring("当前应用未拥有蓝牙设备使用权限"); |
|
||||||
} else { |
|
||||||
permission = true; |
|
||||||
} |
|
||||||
} else { |
|
||||||
permission = true; |
|
||||||
} |
|
||||||
return permission; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* 文件读写权限 |
|
||||||
* @param context |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static boolean isWRPermission(Context context) { |
|
||||||
boolean permission = false; |
|
||||||
if (Build.VERSION.SDK_INT >= 23) { |
|
||||||
int checkReadPhoneStatePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE); |
|
||||||
if (checkReadPhoneStatePermission != PackageManager.PERMISSION_GRANTED) { |
|
||||||
// 弹出对话框接收权限
|
|
||||||
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); |
|
||||||
ToastUtils.showWarring("当前应用未拥有存储设备读写权限"); |
|
||||||
} else if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { |
|
||||||
// 弹出对话框接收权限
|
|
||||||
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); |
|
||||||
ToastUtils.showWarring("当前应用未拥有存储设备读写权限"); |
|
||||||
} else { |
|
||||||
permission = true; |
|
||||||
} |
|
||||||
} else { |
|
||||||
permission = true; |
|
||||||
} |
|
||||||
return permission; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 声音设备权限 |
|
||||||
* @param context |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static boolean isAudioPermission(Context context) { |
|
||||||
boolean permission = false; |
|
||||||
if (Build.VERSION.SDK_INT >= 23) { |
|
||||||
int checkReadPhoneStatePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO); |
|
||||||
if (checkReadPhoneStatePermission != PackageManager.PERMISSION_GRANTED) { |
|
||||||
// 弹出对话框接收权限
|
|
||||||
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.RECORD_AUDIO}, 1); |
|
||||||
ToastUtils.showWarring("当前应用未拥有音频录制权限"); |
|
||||||
} else if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { |
|
||||||
// 弹出对话框接收权限
|
|
||||||
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.RECORD_AUDIO}, 1); |
|
||||||
ToastUtils.showWarring("当前应用未拥有音频录制权限"); |
|
||||||
} else { |
|
||||||
permission = true; |
|
||||||
} |
|
||||||
} else { |
|
||||||
permission = true; |
|
||||||
} |
|
||||||
return permission; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 摄像头权限 |
|
||||||
* @param context |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static boolean isCameraPermission(Context context) { |
|
||||||
boolean permission = false; |
|
||||||
if (Build.VERSION.SDK_INT >= 23) { |
|
||||||
int checkReadPhoneStatePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA); |
|
||||||
if (checkReadPhoneStatePermission != PackageManager.PERMISSION_GRANTED) { |
|
||||||
// 弹出对话框接收权限
|
|
||||||
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.CAMERA}, 1); |
|
||||||
ToastUtils.showWarring("当前应用未拥有调用摄像头权限"); |
|
||||||
} else if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { |
|
||||||
// 弹出对话框接收权限
|
|
||||||
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.CAMERA}, 1); |
|
||||||
ToastUtils.showWarring("当前应用未拥有调用摄像头权限"); |
|
||||||
} else { |
|
||||||
permission = true; |
|
||||||
} |
|
||||||
} else { |
|
||||||
permission = true; |
|
||||||
} |
|
||||||
return permission; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} |
|
@ -1,31 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
|
|
||||||
import java.io.IOException; |
|
||||||
import java.net.InetSocketAddress; |
|
||||||
import java.net.Socket; |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class PingUtil { |
|
||||||
|
|
||||||
public static boolean ping(String host, int port) { |
|
||||||
if (port == 0) port = 80; |
|
||||||
|
|
||||||
Socket connect = new Socket(); |
|
||||||
try { |
|
||||||
connect.connect(new InetSocketAddress(host, port), 10 * 1000); |
|
||||||
return connect.isConnected(); |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
}finally{ |
|
||||||
try { |
|
||||||
connect.close(); |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
@ -1,123 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
|
|
||||||
|
|
||||||
import android.graphics.Bitmap; |
|
||||||
import android.graphics.Canvas; |
|
||||||
|
|
||||||
import com.google.zxing.BarcodeFormat; |
|
||||||
import com.google.zxing.EncodeHintType; |
|
||||||
import com.google.zxing.WriterException; |
|
||||||
import com.google.zxing.common.BitMatrix; |
|
||||||
import com.google.zxing.qrcode.QRCodeWriter; |
|
||||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; |
|
||||||
import xyz.fycz.myreader.application.MyApplication; |
|
||||||
|
|
||||||
import java.io.FileOutputStream; |
|
||||||
import java.io.IOException; |
|
||||||
import java.util.HashMap; |
|
||||||
import java.util.Map; |
|
||||||
|
|
||||||
/** |
|
||||||
* 二维码生成工具类 |
|
||||||
*/ |
|
||||||
public class QRCodeUtil { |
|
||||||
/** |
|
||||||
* 生成二维码Bitmap |
|
||||||
* |
|
||||||
* @param content 内容 |
|
||||||
* @param widthPix 图片宽度 |
|
||||||
* @param heightPix 图片高度 |
|
||||||
* @param logoBm 二维码中心的Logo图标(可以为null) |
|
||||||
* @param filePath 用于存储二维码图片的文件路径 |
|
||||||
* @return 生成二维码及保存文件是否成功 |
|
||||||
*/ |
|
||||||
public static boolean createQRImage(String content, int widthPix, int heightPix, Bitmap logoBm, String filePath) { |
|
||||||
try { |
|
||||||
if (content == null || "".equals(content)) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
//配置参数
|
|
||||||
Map<EncodeHintType, Object> hints = new HashMap<>(); |
|
||||||
hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); |
|
||||||
//容错级别
|
|
||||||
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); |
|
||||||
//设置空白边距的宽度
|
|
||||||
// hints.put(EncodeHintType.MARGIN, 2); //default is 4
|
|
||||||
|
|
||||||
// 图像数据转换,使用了矩阵转换
|
|
||||||
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints); |
|
||||||
int[] pixels = new int[widthPix * heightPix]; |
|
||||||
// 下面这里按照二维码的算法,逐个生成二维码的图片,
|
|
||||||
// 两个for循环是图片横列扫描的结果
|
|
||||||
for (int y = 0; y < heightPix; y++) { |
|
||||||
for (int x = 0; x < widthPix; x++) { |
|
||||||
if (bitMatrix.get(x, y)) { |
|
||||||
pixels[y * widthPix + x] = 0xff000000; |
|
||||||
} else { |
|
||||||
pixels[y * widthPix + x] = 0xffffffff; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// 生成二维码图片的格式,使用ARGB_8888
|
|
||||||
Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888); |
|
||||||
bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix); |
|
||||||
|
|
||||||
if (logoBm != null) { |
|
||||||
bitmap = addLogo(bitmap, logoBm); |
|
||||||
} |
|
||||||
|
|
||||||
//必须使用compress方法将bitmap保存到文件中再进行读取。直接返回的bitmap是没有任何压缩的,内存消耗巨大!
|
|
||||||
FileOutputStream fos = MyApplication.getApplication().openFileOutput(filePath, MyApplication.getApplication().MODE_PRIVATE); |
|
||||||
return bitmap != null && bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); |
|
||||||
} catch (WriterException | IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 在二维码中间添加Logo图案 |
|
||||||
*/ |
|
||||||
private static Bitmap addLogo(Bitmap src, Bitmap logo) { |
|
||||||
if (src == null) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
if (logo == null) { |
|
||||||
return src; |
|
||||||
} |
|
||||||
|
|
||||||
//获取图片的宽高
|
|
||||||
int srcWidth = src.getWidth(); |
|
||||||
int srcHeight = src.getHeight(); |
|
||||||
int logoWidth = logo.getWidth(); |
|
||||||
int logoHeight = logo.getHeight(); |
|
||||||
|
|
||||||
if (srcWidth == 0 || srcHeight == 0) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
if (logoWidth == 0 || logoHeight == 0) { |
|
||||||
return src; |
|
||||||
} |
|
||||||
|
|
||||||
//logo大小为二维码整体大小的1/5
|
|
||||||
float scaleFactor = srcWidth * 1.0f / 5 / logoWidth; |
|
||||||
Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888); |
|
||||||
try { |
|
||||||
Canvas canvas = new Canvas(bitmap); |
|
||||||
canvas.drawBitmap(src, 0, 0, null); |
|
||||||
canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2); |
|
||||||
canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null); |
|
||||||
canvas.save(); |
|
||||||
canvas.restore(); |
|
||||||
} catch (Exception e) { |
|
||||||
bitmap = null; |
|
||||||
e.getStackTrace(); |
|
||||||
} |
|
||||||
return bitmap; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,137 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import android.content.SharedPreferences; |
|
||||||
import android.content.SharedPreferences.Editor; |
|
||||||
import android.preference.PreferenceManager; |
|
||||||
import xyz.fycz.myreader.application.MyApplication; |
|
||||||
|
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.HashSet; |
|
||||||
import java.util.Iterator; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Set; |
|
||||||
|
|
||||||
public class SharedPreferencesUtils { |
|
||||||
|
|
||||||
private static Editor mEditor; |
|
||||||
|
|
||||||
private SharedPreferences mSharedPreferences; |
|
||||||
|
|
||||||
private static SharedPreferencesUtils mSharedPreferencesUtils; |
|
||||||
|
|
||||||
public static SharedPreferencesUtils getInstance() { |
|
||||||
if(mSharedPreferencesUtils == null) { |
|
||||||
mSharedPreferencesUtils = new SharedPreferencesUtils(MyApplication.getmContext(), |
|
||||||
"FYReaderShare"); |
|
||||||
} |
|
||||||
return mSharedPreferencesUtils; |
|
||||||
} |
|
||||||
/** |
|
||||||
* |
|
||||||
* @param context |
|
||||||
*/ |
|
||||||
public SharedPreferencesUtils(Context context){ |
|
||||||
mSharedPreferences = PreferenceManager |
|
||||||
.getDefaultSharedPreferences(context); |
|
||||||
mEditor = mSharedPreferences.edit(); |
|
||||||
} |
|
||||||
/** |
|
||||||
* |
|
||||||
* @param context |
|
||||||
* @param fileName |
|
||||||
*/ |
|
||||||
public SharedPreferencesUtils(Context context, String fileName){ |
|
||||||
mSharedPreferences = context |
|
||||||
.getSharedPreferences(fileName, Context.MODE_PRIVATE); |
|
||||||
mEditor = mSharedPreferences.edit(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置通知公告是否已被查看 |
|
||||||
* @param key |
|
||||||
* @param flag |
|
||||||
*/ |
|
||||||
public void setNoticeFlag(String key, boolean flag) { |
|
||||||
mEditor.putBoolean(key, flag); |
|
||||||
mEditor.commit(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 判断在该机该用户的通知公告是否已被查看 |
|
||||||
* @param key |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public boolean isNoticeFlag(String key){ |
|
||||||
return mSharedPreferences.getBoolean(key,false); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* 保存数据 |
|
||||||
* @param key 键值 |
|
||||||
* @param news 需要保存的数据 |
|
||||||
*/ |
|
||||||
public void save(String key, String news) { |
|
||||||
Set<String> values = null; |
|
||||||
//获取或创建key,如果xml文件中含有key
|
|
||||||
if(mSharedPreferences.contains(key)){ |
|
||||||
//将数据存储到集合里
|
|
||||||
values = mSharedPreferences.getStringSet(key, null); |
|
||||||
//删除xml文件中key,并提交
|
|
||||||
mEditor.remove(key).commit(); |
|
||||||
}else{//如果xml文件中没有key
|
|
||||||
values = new HashSet<String>(); |
|
||||||
} |
|
||||||
//将新的数据添加到集合中
|
|
||||||
values.add(news); |
|
||||||
//将集合保存到偏好设置中
|
|
||||||
mEditor.putStringSet(key, values); |
|
||||||
//提交
|
|
||||||
mEditor.commit(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 清空数据 |
|
||||||
*/ |
|
||||||
public void clear(){ |
|
||||||
mEditor.clear(); |
|
||||||
mEditor.commit(); |
|
||||||
} |
|
||||||
/** |
|
||||||
* 获取数据 |
|
||||||
* @param key 键值 |
|
||||||
* @return 返回一个字符串 |
|
||||||
*/ |
|
||||||
public List<String> query(String key){ |
|
||||||
//创建StringBuffer对象,用来存储从偏好设置中获取的数据
|
|
||||||
List<String> list = new ArrayList<String>(); |
|
||||||
//获取xml中对应key的值
|
|
||||||
Set<String> values = mSharedPreferences.getStringSet(key, null); |
|
||||||
//判断该数据是否为空,如果不为空,则
|
|
||||||
if(key != null && values != null){ |
|
||||||
//遍历集合
|
|
||||||
Iterator<String> iterator = values.iterator(); |
|
||||||
while(iterator.hasNext()){ |
|
||||||
//将数据添加到StringBuffer
|
|
||||||
list.add(iterator.next()); |
|
||||||
} |
|
||||||
} |
|
||||||
return list; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置是否是第一次登录 |
|
||||||
* @param values |
|
||||||
*/ |
|
||||||
public void setFirstEntry(int values){ |
|
||||||
mEditor.putInt("first_entry", values); |
|
||||||
mEditor.commit(); |
|
||||||
} |
|
||||||
|
|
||||||
public int getFirstEntry(){ |
|
||||||
return mSharedPreferences.getInt("first_entry",-1); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
@ -1,98 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
import android.graphics.Rect; |
|
||||||
import android.util.Log; |
|
||||||
import android.view.View; |
|
||||||
import android.view.ViewTreeObserver; |
|
||||||
|
|
||||||
import java.util.LinkedList; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* 软键盘弹出/关闭监听 |
|
||||||
*/ |
|
||||||
|
|
||||||
public class SoftKeyBroadManager implements ViewTreeObserver.OnGlobalLayoutListener{ |
|
||||||
|
|
||||||
public interface SoftKeyboardStateListener { |
|
||||||
void onSoftKeyboardOpened(int keyboardHeightInPx); |
|
||||||
|
|
||||||
void onSoftKeyboardClosed(); |
|
||||||
} |
|
||||||
|
|
||||||
private final List<SoftKeyboardStateListener> listeners = new LinkedList<SoftKeyboardStateListener>(); |
|
||||||
private final View activityRootView; |
|
||||||
private int lastSoftKeyboardHeightInPx; |
|
||||||
private boolean isSoftKeyboardOpened; |
|
||||||
|
|
||||||
public SoftKeyBroadManager(View activityRootView) { |
|
||||||
this(activityRootView,false); |
|
||||||
} |
|
||||||
|
|
||||||
public SoftKeyBroadManager(View activityRootView, boolean isSoftKeyboardOpened) { |
|
||||||
this.activityRootView = activityRootView; |
|
||||||
this.isSoftKeyboardOpened = isSoftKeyboardOpened; |
|
||||||
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onGlobalLayout() { |
|
||||||
final Rect r = new Rect(); |
|
||||||
//r will be populated with the coordinates of your view that area still visible.
|
|
||||||
activityRootView.getWindowVisibleDisplayFrame(r); |
|
||||||
|
|
||||||
final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top); |
|
||||||
Log.d("SoftKeyboardStateHelper","heightDiff:" + heightDiff); |
|
||||||
if (!isSoftKeyboardOpened && heightDiff > 500) { // if more than 100 pixels, its probably a keyboard...
|
|
||||||
isSoftKeyboardOpened = true; |
|
||||||
notifyOnSoftKeyboardOpened(heightDiff); |
|
||||||
//if (isSoftKeyboardOpened && heightDiff < 100)
|
|
||||||
} else if (isSoftKeyboardOpened && heightDiff < 500) { |
|
||||||
isSoftKeyboardOpened = false; |
|
||||||
notifyOnSoftKeyboardClosed(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) { |
|
||||||
this.isSoftKeyboardOpened = isSoftKeyboardOpened; |
|
||||||
} |
|
||||||
|
|
||||||
public boolean isSoftKeyboardOpened() { |
|
||||||
return isSoftKeyboardOpened; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Default value is zero (0) |
|
||||||
* |
|
||||||
* @return last saved keyboard height in px |
|
||||||
*/ |
|
||||||
public int getLastSoftKeyboardHeightInPx() { |
|
||||||
return lastSoftKeyboardHeightInPx; |
|
||||||
} |
|
||||||
|
|
||||||
public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) { |
|
||||||
listeners.add(listener); |
|
||||||
} |
|
||||||
|
|
||||||
public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) { |
|
||||||
listeners.remove(listener); |
|
||||||
} |
|
||||||
|
|
||||||
private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) { |
|
||||||
this.lastSoftKeyboardHeightInPx = keyboardHeightInPx; |
|
||||||
|
|
||||||
for (SoftKeyboardStateListener listener : listeners) { |
|
||||||
if (listener != null) { |
|
||||||
listener.onSoftKeyboardOpened(keyboardHeightInPx); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void notifyOnSoftKeyboardClosed() { |
|
||||||
for (SoftKeyboardStateListener listener : listeners) { |
|
||||||
if (listener != null) { |
|
||||||
listener.onSoftKeyboardClosed(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
@ -1,51 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import android.telephony.TelephonyManager; |
|
||||||
|
|
||||||
import java.lang.reflect.Method; |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class TelephonyUtil { |
|
||||||
|
|
||||||
|
|
||||||
public static String getNum1(Context context){ |
|
||||||
String tel; |
|
||||||
String IMSI; |
|
||||||
try { |
|
||||||
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); |
|
||||||
String deviceid = tm.getDeviceId(); |
|
||||||
tel = tm.getLine1Number();//手机号码
|
|
||||||
Class clazz = tm.getClass(); |
|
||||||
Method getPhoneNumber = clazz.getDeclaredMethod("getLine1NumberForSubscriber",int.class); |
|
||||||
String tel0 = (String)getPhoneNumber.invoke(tm, 0); |
|
||||||
String tel1 = (String)getPhoneNumber.invoke(tm, 1); |
|
||||||
|
|
||||||
IMSI = tm.getSubscriberId(); |
|
||||||
ToastUtils.showInfo(IMSI); |
|
||||||
}catch (Exception e){ |
|
||||||
e.printStackTrace(); |
|
||||||
tel = ""; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
return tel; |
|
||||||
} |
|
||||||
|
|
||||||
public static String getNum2(Context context){ |
|
||||||
String tel; |
|
||||||
try { |
|
||||||
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); |
|
||||||
String deviceid = tm.getDeviceId(); |
|
||||||
tel = tm.getLine1Number();//手机号码
|
|
||||||
|
|
||||||
}catch (Exception e){ |
|
||||||
e.printStackTrace(); |
|
||||||
tel = ""; |
|
||||||
} |
|
||||||
|
|
||||||
return tel; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,5 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
|
|
||||||
|
|
||||||
public class UploadImageTask{ |
|
||||||
} |
|
@ -1,27 +0,0 @@ |
|||||||
package xyz.fycz.myreader.util; |
|
||||||
|
|
||||||
import android.app.Service; |
|
||||||
import android.os.Vibrator; |
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity; |
|
||||||
|
|
||||||
|
|
||||||
public class VibratorUtil { |
|
||||||
|
|
||||||
/** |
|
||||||
* final Activity activity :调用该方法的Activity实例 |
|
||||||
* long milliseconds :震动的时长,单位是毫秒 |
|
||||||
* long[] pattern :自定义震动模式 。数组中数字的含义依次是[静止时长,震动时长,静止时长,震动时长。。。]时长的单位是毫秒 |
|
||||||
* boolean isRepeat : 是否反复震动,如果是true,反复震动,如果是false,只震动一次 |
|
||||||
*/ |
|
||||||
|
|
||||||
public static void Vibrate(final AppCompatActivity activity, long milliseconds) { |
|
||||||
Vibrator vib = (Vibrator) activity.getSystemService(Service.VIBRATOR_SERVICE); |
|
||||||
vib.vibrate(milliseconds); |
|
||||||
} |
|
||||||
public static void Vibrate(final AppCompatActivity activity, long[] pattern, boolean isRepeat) { |
|
||||||
Vibrator vib = (Vibrator) activity.getSystemService(Service.VIBRATOR_SERVICE); |
|
||||||
vib.vibrate(pattern, isRepeat ? 1 : -1); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,43 @@ |
|||||||
|
package xyz.fycz.myreader.util.utils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by newbiechen on 2018/1/1. |
||||||
|
*/ |
||||||
|
|
||||||
|
import java.security.MessageDigest; |
||||||
|
import java.security.NoSuchAlgorithmException; |
||||||
|
|
||||||
|
/** |
||||||
|
* 将字符串转化为MD5 |
||||||
|
*/ |
||||||
|
|
||||||
|
public class MD5Utils { |
||||||
|
|
||||||
|
public static String strToMd5By32(String str) { |
||||||
|
String reStr = null; |
||||||
|
try { |
||||||
|
MessageDigest md5 = MessageDigest.getInstance("MD5"); |
||||||
|
byte[] bytes = md5.digest(str.getBytes()); |
||||||
|
StringBuilder stringBuffer = new StringBuilder(); |
||||||
|
for (byte b : bytes) { |
||||||
|
int bt = b & 0xff; |
||||||
|
if (bt < 16) { |
||||||
|
stringBuffer.append(0); |
||||||
|
} |
||||||
|
stringBuffer.append(Integer.toHexString(bt)); |
||||||
|
} |
||||||
|
reStr = stringBuffer.toString(); |
||||||
|
} catch (NoSuchAlgorithmException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
return reStr; |
||||||
|
} |
||||||
|
|
||||||
|
public static String strToMd5By16(String str) { |
||||||
|
String reStr = strToMd5By32(str); |
||||||
|
if (reStr != null) { |
||||||
|
reStr = reStr.substring(8, 24); |
||||||
|
} |
||||||
|
return reStr; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
package xyz.fycz.myreader.util.utils; |
||||||
|
|
||||||
|
import android.util.Log; |
||||||
|
import okhttp3.*; |
||||||
|
import xyz.fycz.myreader.application.TrustAllCerts; |
||||||
|
import xyz.fycz.myreader.util.HttpUtil; |
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext; |
||||||
|
import javax.net.ssl.SSLSocketFactory; |
||||||
|
import javax.net.ssl.TrustManager; |
||||||
|
import java.io.IOException; |
||||||
|
import java.security.SecureRandom; |
||||||
|
|
||||||
|
public class OkHttpUtils { |
||||||
|
|
||||||
|
public static OkHttpClient okHttpClient = HttpUtil.getOkHttpClient(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 同步获取html文件,默认编码utf-8 |
||||||
|
*/ |
||||||
|
public static String getHtml(String url) throws IOException { |
||||||
|
return getHtml(url, "utf-8"); |
||||||
|
} |
||||||
|
public static String getHtml(String url, String encodeType) throws IOException { |
||||||
|
return getHtml(url, null, encodeType); |
||||||
|
} |
||||||
|
|
||||||
|
public static String getHtml(String url, RequestBody requestBody, String encodeType) throws IOException { |
||||||
|
|
||||||
|
Request.Builder builder = new Request.Builder() |
||||||
|
.addHeader("accept", "*/*") |
||||||
|
.addHeader("connection", "Keep-Alive") |
||||||
|
//.addHeader("Charsert", "utf-8")
|
||||||
|
.addHeader("Cache-Control", "no-cache") |
||||||
|
.addHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"); |
||||||
|
if (requestBody != null) { |
||||||
|
builder.post(requestBody); |
||||||
|
Log.d("HttpPost URl", url); |
||||||
|
}else { |
||||||
|
Log.d("HttpGet URl", url); |
||||||
|
} |
||||||
|
Request request = builder |
||||||
|
.url(url) |
||||||
|
.build(); |
||||||
|
Response response = okHttpClient |
||||||
|
.newCall(request) |
||||||
|
.execute(); |
||||||
|
ResponseBody body=response.body(); |
||||||
|
if (body == null) { |
||||||
|
return ""; |
||||||
|
} else { |
||||||
|
String bodyStr = new String(body.bytes(), encodeType); |
||||||
|
Log.d("Http: read finish", bodyStr); |
||||||
|
return bodyStr; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static SSLSocketFactory createSSLSocketFactory() { |
||||||
|
SSLSocketFactory ssfFactory = null; |
||||||
|
try { |
||||||
|
SSLContext sc = SSLContext.getInstance("TLS"); |
||||||
|
sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom()); |
||||||
|
ssfFactory = sc.getSocketFactory(); |
||||||
|
} catch (Exception e) { |
||||||
|
} |
||||||
|
|
||||||
|
return ssfFactory; |
||||||
|
} |
||||||
|
} |
@ -1,4 +1,4 @@ |
|||||||
package xyz.fycz.myreader.callback; |
package xyz.fycz.myreader.webapi.callback; |
||||||
|
|
||||||
import android.graphics.Bitmap; |
import android.graphics.Bitmap; |
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
package xyz.fycz.myreader.callback; |
package xyz.fycz.myreader.webapi.callback; |
||||||
|
|
||||||
|
|
||||||
import xyz.fycz.myreader.entity.JsonModel; |
import xyz.fycz.myreader.entity.JsonModel; |
@ -1,4 +1,4 @@ |
|||||||
package xyz.fycz.myreader.callback; |
package xyz.fycz.myreader.webapi.callback; |
||||||
|
|
||||||
|
|
||||||
/** |
/** |
@ -1,4 +1,4 @@ |
|||||||
package xyz.fycz.myreader.callback; |
package xyz.fycz.myreader.webapi.callback; |
||||||
|
|
||||||
/** |
/** |
||||||
* Created by zhao on 2016/4/16. |
* Created by zhao on 2016/4/16. |
@ -1,4 +1,4 @@ |
|||||||
package xyz.fycz.myreader.webapi.crawler; |
package xyz.fycz.myreader.webapi.crawler.base; |
||||||
|
|
||||||
import xyz.fycz.myreader.greendao.entity.Book; |
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
package xyz.fycz.myreader.webapi.crawler; |
package xyz.fycz.myreader.webapi.crawler.base; |
||||||
|
|
||||||
import xyz.fycz.myreader.entity.bookstore.BookType; |
import xyz.fycz.myreader.entity.bookstore.BookType; |
||||||
import xyz.fycz.myreader.greendao.entity.Book; |
import xyz.fycz.myreader.greendao.entity.Book; |
@ -1,117 +0,0 @@ |
|||||||
package xyz.fycz.myreader.webapi.crawler.find; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import android.graphics.Bitmap; |
|
||||||
import android.util.Log; |
|
||||||
import android.webkit.*; |
|
||||||
import okhttp3.*; |
|
||||||
import org.json.JSONArray; |
|
||||||
import org.json.JSONException; |
|
||||||
import org.json.JSONObject; |
|
||||||
import xyz.fycz.myreader.application.MyApplication; |
|
||||||
import xyz.fycz.myreader.callback.ResultCallback; |
|
||||||
import xyz.fycz.myreader.creator.DialogCreator; |
|
||||||
import xyz.fycz.myreader.entity.bookstore.BookType; |
|
||||||
import xyz.fycz.myreader.entity.bookstore.QDBook; |
|
||||||
import xyz.fycz.myreader.entity.bookstore.RankBook; |
|
||||||
import xyz.fycz.myreader.entity.bookstore.SortBook; |
|
||||||
import xyz.fycz.myreader.greendao.entity.Book; |
|
||||||
import xyz.fycz.myreader.util.IOUtils; |
|
||||||
import xyz.fycz.myreader.util.SharedPreUtils; |
|
||||||
import xyz.fycz.myreader.util.StringHelper; |
|
||||||
import xyz.fycz.myreader.webapi.CommonApi; |
|
||||||
import xyz.fycz.myreader.webapi.crawler.FindCrawler; |
|
||||||
|
|
||||||
import java.io.BufferedReader; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStreamReader; |
|
||||||
import java.text.SimpleDateFormat; |
|
||||||
import java.util.*; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author fengyue |
|
||||||
* @date 2020/9/16 22:01 |
|
||||||
*/ |
|
||||||
public class ABC extends FindCrawler { |
|
||||||
private String rankUrl = ""; |
|
||||||
private String sortUrl = ""; |
|
||||||
private String[] sex = {"male", "female"}; |
|
||||||
private String aParam = ""; |
|
||||||
private String imgUrl = ""; |
|
||||||
private String defaultCookie = ""; |
|
||||||
private String yearmonthFormat = ""; |
|
||||||
private LinkedHashMap<String, String> rankName = new LinkedHashMap<>(); |
|
||||||
private LinkedHashMap<String, Integer> sortName = new LinkedHashMap<>(); |
|
||||||
private boolean isFemale; |
|
||||||
private boolean isSort; |
|
||||||
|
|
||||||
public ABC(boolean isFemale) { |
|
||||||
this.isFemale = isFemale; |
|
||||||
} |
|
||||||
|
|
||||||
public ABC(boolean isFemale, boolean isSort) { |
|
||||||
this.isFemale = isFemale; |
|
||||||
this.isSort = isSort; |
|
||||||
} |
|
||||||
|
|
||||||
private void initMaleRankName() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
private void initSortNames() { |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
public List<BookType> getRankTypes() { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
public void getRankBooks(BookType bookType, ResultCallback rc) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
private List<QDBook> getBooksFromJson(String json) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public String getCharset() { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public String getFindName() { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public String getFindUrl() { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean getTypePage(BookType curType, int page) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean hasImg() { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public List<BookType> getBookTypeList(String html) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public List<Book> getRankBookList(String html) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
public void initCookie(Context mContext, ResultCallback rc) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,270 @@ |
|||||||
|
package xyz.fycz.myreader.webapi.crawler.find; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import org.json.JSONArray; |
||||||
|
import org.json.JSONException; |
||||||
|
import org.json.JSONObject; |
||||||
|
import xyz.fycz.myreader.application.MyApplication; |
||||||
|
import xyz.fycz.myreader.webapi.callback.ResultCallback; |
||||||
|
import xyz.fycz.myreader.entity.bookstore.BookType; |
||||||
|
import xyz.fycz.myreader.entity.bookstore.QDBook; |
||||||
|
import xyz.fycz.myreader.entity.bookstore.RankBook; |
||||||
|
import xyz.fycz.myreader.entity.bookstore.SortBook; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.util.IOUtils; |
||||||
|
import xyz.fycz.myreader.util.SharedPreUtils; |
||||||
|
import xyz.fycz.myreader.util.StringHelper; |
||||||
|
import xyz.fycz.myreader.webapi.CommonApi; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.FindCrawler; |
||||||
|
|
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStreamReader; |
||||||
|
import java.text.SimpleDateFormat; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2020/9/16 22:01 |
||||||
|
*/ |
||||||
|
public class QiDianMobileRank extends FindCrawler { |
||||||
|
private String rankUrl = "https://m.qidian.com/majax/rank/{rankName}list?_csrfToken={cookie}&gender={sex}&pageNum={page}&catId=-1"; |
||||||
|
private String sortUrl = "https://m.qidian.com/majax/category/list?_csrfToken={cookie}&gender={sex}&pageNum={page}&orderBy=&catId={catId}&subCatId="; |
||||||
|
private String[] sex = {"male", "female"}; |
||||||
|
private String yuepiaoParam = "&yearmonth={yearmonth}"; |
||||||
|
private String imgUrl = "https://bookcover.yuewen.com/qdbimg/349573/{bid}/150"; |
||||||
|
private String defaultCookie = "eXRDlZxmRDLvFAmdgzqvwWAASrxxp2WkVlH4ZM7e"; |
||||||
|
private String yearmonthFormat = "yyyyMM"; |
||||||
|
private LinkedHashMap<String, String> rankName = new LinkedHashMap<>(); |
||||||
|
private LinkedHashMap<String, Integer> sortName = new LinkedHashMap<>(); |
||||||
|
private boolean isFemale; |
||||||
|
private boolean isSort; |
||||||
|
|
||||||
|
public QiDianMobileRank(boolean isFemale) { |
||||||
|
this.isFemale = isFemale; |
||||||
|
} |
||||||
|
|
||||||
|
public QiDianMobileRank(boolean isFemale, boolean isSort) { |
||||||
|
this.isFemale = isFemale; |
||||||
|
this.isSort = isSort; |
||||||
|
} |
||||||
|
|
||||||
|
private void initMaleRankName() { |
||||||
|
if (!isFemale) { |
||||||
|
rankName.put("风云榜", "yuepiao"); |
||||||
|
rankName.put("畅销榜", "hotsales"); |
||||||
|
rankName.put("阅读榜", "readIndex"); |
||||||
|
rankName.put("粉丝榜", "newfans"); |
||||||
|
rankName.put("推荐榜", "rec"); |
||||||
|
rankName.put("更新榜", "update"); |
||||||
|
rankName.put("签约榜", "sign"); |
||||||
|
rankName.put("新书榜", "newbook"); |
||||||
|
rankName.put("新人榜", "newauthor"); |
||||||
|
} else { |
||||||
|
rankName.put("风云榜", "yuepiao"); |
||||||
|
rankName.put("阅读榜", "readIndex"); |
||||||
|
rankName.put("粉丝榜", "newfans"); |
||||||
|
rankName.put("推荐榜", "rec"); |
||||||
|
rankName.put("更新榜", "update"); |
||||||
|
rankName.put("收藏榜", "collect"); |
||||||
|
rankName.put("免费榜", "free"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void initSortNames() { |
||||||
|
/* |
||||||
|
{value: -1, text: "全站"} |
||||||
|
1: {value: 21, text: "玄幻"} |
||||||
|
2: {value: 1, text: "奇幻"} |
||||||
|
3: {value: 2, text: "武侠"} |
||||||
|
4: {value: 22, text: "仙侠"} |
||||||
|
5: {value: 4, text: "都市"} |
||||||
|
6: {value: 15, text: "现实"} |
||||||
|
7: {value: 6, text: "军事"} |
||||||
|
8: {value: 5, text: "历史"} |
||||||
|
9: {value: 7, text: "游戏"} |
||||||
|
10: {value: 8, text: "体育"} |
||||||
|
11: {value: 9, text: "科幻"} |
||||||
|
12: {value: 10, text: "悬疑"} |
||||||
|
13: {value: 12, text: "轻小说"} |
||||||
|
*/ |
||||||
|
if (!isFemale) { |
||||||
|
sortName.put("玄幻小说", 21); |
||||||
|
sortName.put("奇幻小说", 1); |
||||||
|
sortName.put("武侠小说", 2); |
||||||
|
sortName.put("都市小说", 4); |
||||||
|
sortName.put("现实小说", 15); |
||||||
|
sortName.put("军事小说", 6); |
||||||
|
sortName.put("历史小说", 5); |
||||||
|
sortName.put("体育小说", 8); |
||||||
|
sortName.put("科幻小说", 9); |
||||||
|
sortName.put("悬疑小说", 10); |
||||||
|
sortName.put("轻小说", 12); |
||||||
|
sortName.put("短篇小说", 20076); |
||||||
|
} else { |
||||||
|
sortName.put("古代言情", 80); |
||||||
|
sortName.put("仙侠奇缘", 81); |
||||||
|
sortName.put("现代言情", 82); |
||||||
|
sortName.put("烂漫青春", 83); |
||||||
|
sortName.put("玄幻言情", 84); |
||||||
|
sortName.put("悬疑推理", 85); |
||||||
|
sortName.put("短篇小说", 30083); |
||||||
|
sortName.put("科幻空间", 86); |
||||||
|
sortName.put("游戏竞技", 88); |
||||||
|
sortName.put("轻小说", 87); |
||||||
|
sortName.put("现实生活", 30120); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public List<BookType> getRankTypes() { |
||||||
|
if (!isSort) { |
||||||
|
initMaleRankName(); |
||||||
|
} else { |
||||||
|
initSortNames(); |
||||||
|
} |
||||||
|
List<BookType> bookTypes = new ArrayList<>(); |
||||||
|
Set<String> names = !isSort ? rankName.keySet() : sortName.keySet(); |
||||||
|
for (String name : names) { |
||||||
|
BookType bookType = new BookType(); |
||||||
|
bookType.setTypeName(name); |
||||||
|
String url; |
||||||
|
if (!isSort) { |
||||||
|
url = rankUrl.replace("{rankName}", rankName.get(name)); |
||||||
|
} else { |
||||||
|
url = sortUrl.replace("{catId}", sortName.get(name) + ""); |
||||||
|
} |
||||||
|
url = url.replace("{sex}", !isFemale ? sex[0] : sex[1]); |
||||||
|
SharedPreUtils spu = SharedPreUtils.getInstance(); |
||||||
|
String cookie = spu.getString("qdCookie", ""); |
||||||
|
if (!cookie.equals("")) { |
||||||
|
url = url.replace("{cookie}", StringHelper.getSubString(cookie, "_csrfToken=", ";")); |
||||||
|
} else { |
||||||
|
url = url.replace("{cookie}", defaultCookie); |
||||||
|
} |
||||||
|
if ("风云榜".equals(name)) { |
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat(yearmonthFormat, Locale.CHINA); |
||||||
|
String yearmonth = sdf.format(new Date()); |
||||||
|
url = url + yuepiaoParam.replace("{yearmonth}", yearmonth); |
||||||
|
} |
||||||
|
bookType.setUrl(url); |
||||||
|
bookTypes.add(bookType); |
||||||
|
} |
||||||
|
return bookTypes; |
||||||
|
} |
||||||
|
|
||||||
|
public void getRankBooks(BookType bookType, ResultCallback rc) { |
||||||
|
CommonApi.getCommonReturnHtmlStringApi(bookType.getUrl(), null, "UTF-8", true, |
||||||
|
new ResultCallback() { |
||||||
|
@Override |
||||||
|
public void onFinish(Object o, int code) { |
||||||
|
rc.onFinish(getBooksFromJson((String) o), 1); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onError(Exception e) { |
||||||
|
rc.onError(e); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private List<QDBook> getBooksFromJson(String json) { |
||||||
|
List<QDBook> books = new ArrayList<>(); |
||||||
|
try { |
||||||
|
JSONObject all = new JSONObject(json); |
||||||
|
JSONObject data = all.getJSONObject("data"); |
||||||
|
JSONArray jsonBooks = data.getJSONArray("records"); |
||||||
|
for (int i = 0; i < jsonBooks.length(); i++) { |
||||||
|
JSONObject jsonBook = jsonBooks.getJSONObject(i); |
||||||
|
QDBook book = !isSort ? new RankBook() : new SortBook(); |
||||||
|
book.setbName(jsonBook.getString("bName")); |
||||||
|
book.setbAuth(jsonBook.getString("bAuth")); |
||||||
|
book.setBid(jsonBook.getString("bid")); |
||||||
|
book.setCat(jsonBook.getString("cat")); |
||||||
|
book.setCatId(jsonBook.getInt("catId")); |
||||||
|
book.setCnt(jsonBook.getString("cnt")); |
||||||
|
book.setDesc(jsonBook.getString("desc")); |
||||||
|
book.setImg(imgUrl.replace("{bid}", jsonBook.getString("bid"))); |
||||||
|
if (!isSort) { |
||||||
|
((RankBook) book).setRankCnt(jsonBook.getString("rankCnt")); |
||||||
|
((RankBook) book).setRankNum(jsonBook.getInt("rankNum")); |
||||||
|
}else { |
||||||
|
((SortBook) book).setState(jsonBook.getString("state")); |
||||||
|
} |
||||||
|
books.add(book); |
||||||
|
} |
||||||
|
} catch (JSONException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
return books; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getCharset() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getFindName() { |
||||||
|
return !isFemale ? !isSort ? "排行榜[男生小说]" : "分类[男生小说]" : !isSort ? "排行榜[女生小说]" : "分类[女生小说]"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getFindUrl() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean getTypePage(BookType curType, int page) { |
||||||
|
if (!isSort) { |
||||||
|
if (page > 30) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
}else { |
||||||
|
if (page > 5) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
String pageNum = curType.getUrl().substring(curType.getUrl().indexOf("pageNum=") + 8, curType.getUrl().indexOf("&catId")); |
||||||
|
curType.setUrl(curType.getUrl().replace("pageNum=" + pageNum, "pageNum=" + page)); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean hasImg() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<BookType> getBookTypeList(String html) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<Book> getRankBookList(String html) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void initCookie(Context mContext, ResultCallback rc) { |
||||||
|
MyApplication.getApplication().newThread(() -> { |
||||||
|
BufferedReader br = null; |
||||||
|
try { |
||||||
|
br = new BufferedReader(new InputStreamReader(mContext.getAssets().open("_csrfToken.fy"))); |
||||||
|
StringBuilder assetText = new StringBuilder(); |
||||||
|
String line; |
||||||
|
while ((line = br.readLine()) != null) { |
||||||
|
assetText.append(line); |
||||||
|
} |
||||||
|
String[] _csrfTokens = assetText.toString().split(","); |
||||||
|
Random random = new Random(); |
||||||
|
rc.onFinish(_csrfTokens[random.nextInt(_csrfTokens.length)], 1); |
||||||
|
} catch (IOException e) { |
||||||
|
rc.onError(e); |
||||||
|
} finally { |
||||||
|
IOUtils.close(br); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,151 @@ |
|||||||
|
package xyz.fycz.myreader.webapi.crawler.read; |
||||||
|
|
||||||
|
import android.text.Html; |
||||||
|
import org.jsoup.Jsoup; |
||||||
|
import org.jsoup.nodes.Document; |
||||||
|
import org.jsoup.nodes.Element; |
||||||
|
import org.jsoup.select.Elements; |
||||||
|
import xyz.fycz.myreader.entity.SearchBookBean; |
||||||
|
import xyz.fycz.myreader.enums.BookSource; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Chapter; |
||||||
|
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
|
||||||
|
public class Ben100ReadCrawler implements ReadCrawler, BookInfoCrawler { |
||||||
|
public static final String NAME_SPACE = "https://www.100ben.net"; |
||||||
|
public static final String NOVEL_SEARCH = "https://www.100ben.net/plus/search.php?keyword={key}"; |
||||||
|
public static final String CHARSET = "UTF-8"; |
||||||
|
public static final String SEARCH_CHARSET = "utf-8"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSearchLink() { |
||||||
|
return NOVEL_SEARCH; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getCharset() { |
||||||
|
return CHARSET; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getNameSpace() { |
||||||
|
return NAME_SPACE; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Boolean isPost() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSearchCharset() { |
||||||
|
return SEARCH_CHARSET; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从html中获取章节正文 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getContentFormHtml(String html) { |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element divContent = doc.getElementById("content"); |
||||||
|
if (divContent != null) { |
||||||
|
String content = Html.fromHtml(divContent.html()).toString(); |
||||||
|
char c = 160; |
||||||
|
String spaec = "" + c; |
||||||
|
content = content.replace(spaec, " "); |
||||||
|
return content; |
||||||
|
} else { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从html中获取章节列表 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ArrayList<Chapter> getChaptersFromHtml(String html) { |
||||||
|
ArrayList<Chapter> chapters = new ArrayList<>(); |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
try { |
||||||
|
Element divList = doc.getElementById("dir"); |
||||||
|
int i = 0; |
||||||
|
Elements elementsByTag = divList.getElementsByTag("dd"); |
||||||
|
for (int j = 0; j < elementsByTag.size(); j++) { |
||||||
|
Element dd = elementsByTag.get(j); |
||||||
|
Elements as = dd.getElementsByTag("a"); |
||||||
|
Element a = as.get(0); |
||||||
|
String title = a.text(); |
||||||
|
Chapter chapter = new Chapter(); |
||||||
|
chapter.setNumber(i++); |
||||||
|
chapter.setTitle(title); |
||||||
|
String url = NAME_SPACE + a.attr("href"); |
||||||
|
chapter.setUrl(url); |
||||||
|
chapters.add(chapter); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
return chapters; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从搜索html中得到书列表 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) { |
||||||
|
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>(); |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
// try {
|
||||||
|
Elements divs = doc.getElementsByClass("recommand"); |
||||||
|
Element div = divs.get(0); |
||||||
|
Elements elementsByTag = div.getElementsByTag("li"); |
||||||
|
for (Element element : elementsByTag) { |
||||||
|
Book book = new Book(); |
||||||
|
String name = element.getElementsByClass("titles").first().getElementsByTag("a").first().text(); |
||||||
|
book.setName(name); |
||||||
|
String author = element.getElementsByClass("author").first().text().replace("作者:", ""); |
||||||
|
book.setAuthor(author); |
||||||
|
String imgUrl = element.getElementsByTag("img").first().attr("src"); |
||||||
|
book.setImgUrl(imgUrl); |
||||||
|
String chapterUrl = element.getElementsByClass("titles").first().getElementsByTag("a").first().attr("href"); |
||||||
|
book.setChapterUrl(NAME_SPACE + chapterUrl); |
||||||
|
String desc = element.getElementsByClass("intro").first().text(); |
||||||
|
book.setDesc(desc); |
||||||
|
book.setNewestChapterTitle(""); |
||||||
|
book.setSource(BookSource.ben100.toString()); |
||||||
|
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor()); |
||||||
|
books.add(sbb, book); |
||||||
|
} |
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
return books; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取书籍详细信息 |
||||||
|
* @param book |
||||||
|
*/ |
||||||
|
public Book getBookInfo(String html, Book book){ |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element img = doc.getElementById("fmimg"); |
||||||
|
book.setImgUrl(img.getElementsByTag("img").get(0).attr("src")); |
||||||
|
Element desc = doc.getElementById("intro"); |
||||||
|
book.setDesc(desc.getElementsByTag("p").get(0).text()); |
||||||
|
Element type = doc.getElementsByClass("con_top").get(0); |
||||||
|
book.setType(type.getElementsByTag("a").get(2).text()); |
||||||
|
return book; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,139 @@ |
|||||||
|
package xyz.fycz.myreader.webapi.crawler.read; |
||||||
|
|
||||||
|
import android.text.Html; |
||||||
|
import org.jsoup.Jsoup; |
||||||
|
import org.jsoup.nodes.Document; |
||||||
|
import org.jsoup.nodes.Element; |
||||||
|
import org.jsoup.select.Elements; |
||||||
|
import xyz.fycz.myreader.entity.SearchBookBean; |
||||||
|
import xyz.fycz.myreader.enums.BookSource; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Chapter; |
||||||
|
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
|
||||||
|
public class CansShu99ReadCrawler implements ReadCrawler { |
||||||
|
public static final String NAME_SPACE = "http://www.99csw.com"; |
||||||
|
public static final String NOVEL_SEARCH = "http://www.99csw.com/book/search.php?type=all&keyword={key}"; |
||||||
|
public static final String CHARSET = "UTF-8"; |
||||||
|
public static final String SEARCH_CHARSET = "utf-8"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSearchLink() { |
||||||
|
return NOVEL_SEARCH; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getCharset() { |
||||||
|
return CHARSET; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getNameSpace() { |
||||||
|
return NAME_SPACE; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Boolean isPost() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSearchCharset() { |
||||||
|
return SEARCH_CHARSET; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从html中获取章节正文 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getContentFormHtml(String html) { |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element divContent = doc.getElementById("content"); |
||||||
|
if (divContent != null) { |
||||||
|
String content = Html.fromHtml(divContent.html()).toString(); |
||||||
|
char c = 160; |
||||||
|
String spaec = "" + c; |
||||||
|
content = content.replace(spaec, " "); |
||||||
|
return content; |
||||||
|
} else { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从html中获取章节列表 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ArrayList<Chapter> getChaptersFromHtml(String html) { |
||||||
|
ArrayList<Chapter> chapters = new ArrayList<>(); |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
try { |
||||||
|
Element divList = doc.getElementById("dir"); |
||||||
|
int i = 0; |
||||||
|
Elements elementsByTag = divList.getElementsByTag("dd"); |
||||||
|
for (int j = 0; j < elementsByTag.size(); j++) { |
||||||
|
Element dd = elementsByTag.get(j); |
||||||
|
Elements as = dd.getElementsByTag("a"); |
||||||
|
Element a = as.get(0); |
||||||
|
String title = a.text(); |
||||||
|
Chapter chapter = new Chapter(); |
||||||
|
chapter.setNumber(i++); |
||||||
|
chapter.setTitle(title); |
||||||
|
String url = NAME_SPACE + a.attr("href"); |
||||||
|
chapter.setUrl(url); |
||||||
|
chapters.add(chapter); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
return chapters; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从搜索html中得到书列表 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) { |
||||||
|
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>(); |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
// try {
|
||||||
|
Elements divs = doc.getElementsByClass("list_box"); |
||||||
|
Element div = divs.get(0); |
||||||
|
Elements elementsByTag = div.getElementsByTag("li"); |
||||||
|
for (Element element : elementsByTag) { |
||||||
|
Book book = new Book(); |
||||||
|
String name = element.getElementsByTag("h2").first().getElementsByTag("a").first().text(); |
||||||
|
book.setName(name); |
||||||
|
String author = element.getElementsByTag("h4").first().getElementsByTag("a").first().text(); |
||||||
|
book.setAuthor(author); |
||||||
|
String type = element.getElementsByTag("h4").get(1).getElementsByTag("a").text(); |
||||||
|
book.setType(type); |
||||||
|
String desc = element.getElementsByClass("intro").first().text(); |
||||||
|
book.setDesc(desc); |
||||||
|
String imgUrl = element.getElementsByTag("img").first().attr("src"); |
||||||
|
book.setImgUrl("http:" + imgUrl); |
||||||
|
String chapterUrl = element.getElementsByTag("h2").first().getElementsByTag("a").first().attr("href"); |
||||||
|
book.setChapterUrl(NAME_SPACE + chapterUrl); |
||||||
|
book.setNewestChapterTitle(""); |
||||||
|
book.setSource(BookSource.cangshu99.toString()); |
||||||
|
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor()); |
||||||
|
books.add(sbb, book); |
||||||
|
} |
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
return books; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,130 @@ |
|||||||
|
package xyz.fycz.myreader.webapi.crawler.read; |
||||||
|
|
||||||
|
import android.text.Html; |
||||||
|
import org.json.JSONArray; |
||||||
|
import org.json.JSONException; |
||||||
|
import org.json.JSONObject; |
||||||
|
import org.jsoup.Jsoup; |
||||||
|
import org.jsoup.nodes.Document; |
||||||
|
import org.jsoup.nodes.Element; |
||||||
|
import org.jsoup.select.Elements; |
||||||
|
import xyz.fycz.myreader.entity.SearchBookBean; |
||||||
|
import xyz.fycz.myreader.enums.BookSource; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Chapter; |
||||||
|
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
|
||||||
|
public class ChaoXingReadCrawler implements ReadCrawler { |
||||||
|
public static final String NAME_SPACE = "http://yz4.chaoxing.com"; |
||||||
|
public static final String NOVEL_SEARCH = "http://yz4.chaoxing.com/circlemarket/getsearch,start=0&size=25&sw={key}&channelId=52"; |
||||||
|
public static final String CHAPTERS_URL = "https://special.zhexuezj.cn/mobile/mooc/tocourse/"; |
||||||
|
public static final String DESC = "★★★ 超星·出版 ★★★\n★★★ 本书暂无简介 ★★★"; |
||||||
|
public static final String CHARSET = "UTF-8"; |
||||||
|
public static final String SEARCH_CHARSET = "UTF-8"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSearchLink() { |
||||||
|
return NOVEL_SEARCH; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getCharset() { |
||||||
|
return CHARSET; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getNameSpace() { |
||||||
|
return NAME_SPACE; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Boolean isPost() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSearchCharset() { |
||||||
|
return SEARCH_CHARSET; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从html中获取章节正文 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getContentFormHtml(String html) { |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element divContent = doc.getElementById("contentBox"); |
||||||
|
Elements ps = divContent.getElementsByTag("p"); |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
for (Element p : ps){ |
||||||
|
String content = Html.fromHtml(p.html()).toString(); |
||||||
|
char c = 160; |
||||||
|
String spaec = "" + c; |
||||||
|
content = content.replace(spaec, " "); |
||||||
|
sb.append(content); |
||||||
|
sb.append("\n"); |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从html中获取章节列表 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ArrayList<Chapter> getChaptersFromHtml(String html) { |
||||||
|
ArrayList<Chapter> chapters = new ArrayList<>(); |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element divList = doc.getElementsByClass("con").first(); |
||||||
|
Elements elementsByTag = divList.getElementsByTag("a"); |
||||||
|
int i = 0; |
||||||
|
for (Element a : elementsByTag) { |
||||||
|
String title = a.text(); |
||||||
|
String url = a.attr("attr"); |
||||||
|
Chapter chapter = new Chapter(); |
||||||
|
chapter.setNumber(i++); |
||||||
|
chapter.setTitle(title); |
||||||
|
chapter.setUrl(url); |
||||||
|
chapters.add(chapter); |
||||||
|
} |
||||||
|
return chapters; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从搜索html中得到书列表 |
||||||
|
* |
||||||
|
* @param json |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String json) { |
||||||
|
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>(); |
||||||
|
try { |
||||||
|
JSONArray booksArray = new JSONArray(json); |
||||||
|
for (int i = 0; i < booksArray.length(); i++) { |
||||||
|
JSONObject bookJson = booksArray.getJSONObject(i); |
||||||
|
Book book = new Book(); |
||||||
|
book.setName(bookJson.getString("name")); |
||||||
|
book.setAuthor(bookJson.getString("author")); |
||||||
|
book.setImgUrl(bookJson.getString("coverUrl")); |
||||||
|
book.setNewestChapterTitle(""); |
||||||
|
book.setChapterUrl(CHAPTERS_URL + bookJson.getInt("course_Id")); |
||||||
|
book.setDesc(DESC); |
||||||
|
book.setSource(BookSource.chaoxing.toString()); |
||||||
|
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor()); |
||||||
|
books.add(sbb, book); |
||||||
|
} |
||||||
|
} catch (JSONException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
return books; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,127 @@ |
|||||||
|
package xyz.fycz.myreader.webapi.crawler.read; |
||||||
|
|
||||||
|
import android.text.Html; |
||||||
|
import org.jsoup.Jsoup; |
||||||
|
import org.jsoup.nodes.Document; |
||||||
|
import org.jsoup.nodes.Element; |
||||||
|
import org.jsoup.select.Elements; |
||||||
|
import xyz.fycz.myreader.entity.SearchBookBean; |
||||||
|
import xyz.fycz.myreader.enums.BookSource; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Chapter; |
||||||
|
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
|
||||||
|
public class JiuTaoReadCrawler implements ReadCrawler { |
||||||
|
public static final String NAME_SPACE = "https://www.9txs.com"; |
||||||
|
public static final String NOVEL_SEARCH = "https://www.9txs.com/search.html,searchkey={key}"; |
||||||
|
public static final String CHARSET = "UTF-8"; |
||||||
|
public static final String SEARCH_CHARSET = "UTF-8"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSearchLink() { |
||||||
|
return NOVEL_SEARCH; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getCharset() { |
||||||
|
return CHARSET; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getNameSpace() { |
||||||
|
return NAME_SPACE; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Boolean isPost() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSearchCharset() { |
||||||
|
return SEARCH_CHARSET; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从html中获取章节正文 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getContentFormHtml(String html) { |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element divContent = doc.getElementById("content"); |
||||||
|
if (divContent != null) { |
||||||
|
String content = Html.fromHtml(divContent.html()).toString(); |
||||||
|
char c = 160; |
||||||
|
String spaec = "" + c; |
||||||
|
content = content.replace(spaec, " ").replaceAll("您可以在.*最新章节!|\\\\", ""); |
||||||
|
return content; |
||||||
|
} else { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从html中获取章节列表 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ArrayList<Chapter> getChaptersFromHtml(String html) { |
||||||
|
ArrayList<Chapter> chapters = new ArrayList<>(); |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element divList = doc.getElementsByClass("read").first(); |
||||||
|
Elements elementsByTag = divList.getElementsByTag("a"); |
||||||
|
int i = 0; |
||||||
|
for (int j = 12; j < elementsByTag.size(); j++) { |
||||||
|
Element a = elementsByTag.get(j); |
||||||
|
String title = a.text(); |
||||||
|
String url = a.attr("href"); |
||||||
|
Chapter chapter = new Chapter(); |
||||||
|
chapter.setNumber(i++); |
||||||
|
chapter.setTitle(title); |
||||||
|
chapter.setUrl(NAME_SPACE + url); |
||||||
|
chapters.add(chapter); |
||||||
|
} |
||||||
|
return chapters; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从搜索html中得到书列表 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) { |
||||||
|
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>(); |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
// try {
|
||||||
|
Element div = doc.getElementsByClass("library").first(); |
||||||
|
Elements lis = div.getElementsByTag("li"); |
||||||
|
for (Element li : lis){ |
||||||
|
Elements as = li.getElementsByTag("a"); |
||||||
|
Book book = new Book(); |
||||||
|
book.setName(as.get(1).text()); |
||||||
|
book.setAuthor(as.get(2).text()); |
||||||
|
book.setType(as.get(3).text()); |
||||||
|
book.setNewestChapterTitle(as.get(4).text().replace("最新章节:", "")); |
||||||
|
book.setDesc(li.getElementsByClass("intro").first().text()); |
||||||
|
book.setImgUrl(li.getElementsByTag("img").attr("src")); |
||||||
|
book.setChapterUrl(NAME_SPACE + as.get(1).attr("href").replace(".html", "/")); |
||||||
|
book.setSource(BookSource.jiutao.toString()); |
||||||
|
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor()); |
||||||
|
books.add(sbb, book); |
||||||
|
} |
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
return books; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,140 @@ |
|||||||
|
package xyz.fycz.myreader.webapi.crawler.read; |
||||||
|
|
||||||
|
import android.text.Html; |
||||||
|
import org.jsoup.Jsoup; |
||||||
|
import org.jsoup.nodes.Document; |
||||||
|
import org.jsoup.nodes.Element; |
||||||
|
import org.jsoup.select.Elements; |
||||||
|
import xyz.fycz.myreader.entity.SearchBookBean; |
||||||
|
import xyz.fycz.myreader.enums.BookSource; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Chapter; |
||||||
|
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
|
||||||
|
public class MiQuReadCrawler implements ReadCrawler, BookInfoCrawler { |
||||||
|
public static final String NAME_SPACE = "https://www.meegoq.com/"; |
||||||
|
public static final String NOVEL_SEARCH = "https://www.meegoq.com/search.htm?keyword={key}"; |
||||||
|
public static final String CHARSET = "UTF-8"; |
||||||
|
public static final String SEARCH_CHARSET = "UTF-8"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSearchLink() { |
||||||
|
return NOVEL_SEARCH; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getCharset() { |
||||||
|
return CHARSET; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getNameSpace() { |
||||||
|
return NAME_SPACE; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Boolean isPost() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSearchCharset() { |
||||||
|
return SEARCH_CHARSET; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从html中获取章节正文 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getContentFormHtml(String html) { |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element divContent = doc.getElementById("content"); |
||||||
|
if (divContent != null) { |
||||||
|
String content = Html.fromHtml(divContent.html()).toString(); |
||||||
|
char c = 160; |
||||||
|
String spaec = "" + c; |
||||||
|
content = content.replace(spaec, " ").replace("applyChapterSetting();", ""); |
||||||
|
return content; |
||||||
|
} else { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从html中获取章节列表 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ArrayList<Chapter> getChaptersFromHtml(String html) { |
||||||
|
ArrayList<Chapter> chapters = new ArrayList<>(); |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element divList = doc.getElementsByClass("mulu").first(); |
||||||
|
Elements elementsByTag = divList.getElementsByTag("a"); |
||||||
|
int i = 0; |
||||||
|
for (int j = 9; j < elementsByTag.size(); j++) { |
||||||
|
Element a = elementsByTag.get(j); |
||||||
|
String title = a.text(); |
||||||
|
String url = "http:" + a.attr("href"); |
||||||
|
Chapter chapter = new Chapter(); |
||||||
|
chapter.setNumber(i++); |
||||||
|
chapter.setTitle(title); |
||||||
|
chapter.setUrl(url); |
||||||
|
chapters.add(chapter); |
||||||
|
} |
||||||
|
return chapters; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从搜索html中得到书列表 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) { |
||||||
|
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>(); |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Elements divs = doc.getElementsByClass("lastest"); |
||||||
|
Element div = divs.get(0); |
||||||
|
Elements elementsByTag = div.getElementsByTag("li"); |
||||||
|
for (int i = 1; i < elementsByTag.size() - 1; i++) { |
||||||
|
Element element = elementsByTag.get(i); |
||||||
|
Book book = new Book(); |
||||||
|
Element info = element.getElementsByClass("n2").first(); |
||||||
|
book.setName(info.text()); |
||||||
|
book.setInfoUrl("http:" + info.getElementsByTag("a").attr("href")); |
||||||
|
book.setChapterUrl("http:" + info.getElementsByTag("a").attr("href").replace("info", "book")); |
||||||
|
book.setAuthor(element.getElementsByClass("a2").first().text()); |
||||||
|
book.setType(element.getElementsByClass("nt").first().text()); |
||||||
|
book.setNewestChapterTitle(element.getElementsByClass("c2").first().text()); |
||||||
|
book.setSource(BookSource.miqu.toString()); |
||||||
|
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor()); |
||||||
|
books.add(sbb, book); |
||||||
|
} |
||||||
|
return books; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取书籍详细信息 |
||||||
|
* |
||||||
|
* @param book |
||||||
|
*/ |
||||||
|
public Book getBookInfo(String html, Book book) { |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element img = doc.getElementsByClass("cover").first(); |
||||||
|
book.setImgUrl(img.getElementsByTag("img").get(0).attr("src")); |
||||||
|
|
||||||
|
String desc = doc.select("meta[property=og:description]").attr("content"); |
||||||
|
book.setDesc(desc); |
||||||
|
|
||||||
|
return book; |
||||||
|
} |
||||||
|
|
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue