parent
36a2647ccb
commit
44e0be9ab8
Binary file not shown.
@ -1,7 +1,7 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<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" /> |
||||
</component> |
||||
</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.callback.ResultCallback; |
||||
import xyz.fycz.myreader.webapi.callback.ResultCallback; |
||||
import xyz.fycz.myreader.common.APPCONST; |
||||
import xyz.fycz.myreader.common.URLCONST; |
||||
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.LinkedHashMap; |
@ -1,4 +1,4 @@ |
||||
package xyz.fycz.myreader.mulvalmap; |
||||
package xyz.fycz.myreader.model.mulvalmap; |
||||
|
||||
import java.util.List; |
||||
import java.util.Map; |
@ -1,191 +1,81 @@ |
||||
package xyz.fycz.myreader.ui.adapter; |
||||
|
||||
import android.app.Activity; |
||||
import android.content.Context; |
||||
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 android.text.TextUtils; |
||||
import xyz.fycz.myreader.application.MyApplication; |
||||
import xyz.fycz.myreader.callback.ResultCallback; |
||||
import xyz.fycz.myreader.webapi.crawler.BookInfoCrawler; |
||||
import xyz.fycz.myreader.webapi.crawler.ReadCrawler; |
||||
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil; |
||||
import xyz.fycz.myreader.custom.DragAdapter; |
||||
import xyz.fycz.myreader.base.adapter.BaseListAdapter; |
||||
import xyz.fycz.myreader.base.adapter.IViewHolder; |
||||
import xyz.fycz.myreader.entity.SearchBookBean; |
||||
import xyz.fycz.myreader.enums.BookSource; |
||||
import xyz.fycz.myreader.greendao.entity.Book; |
||||
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap; |
||||
import xyz.fycz.myreader.util.StringHelper; |
||||
import xyz.fycz.myreader.webapi.CommonApi; |
||||
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; |
||||
import xyz.fycz.myreader.ui.adapter.holder.SearchBookHolder; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
|
||||
|
||||
/** |
||||
* Created by zhao on 2017/7/26. |
||||
* @author fengyue |
||||
* @date 2020/10/2 10:08 |
||||
*/ |
||||
|
||||
public class SearchBookAdapter extends DragAdapter { |
||||
|
||||
private int mResourceId; |
||||
|
||||
private Context mContext; |
||||
|
||||
public class SearchBookAdapter extends BaseListAdapter<SearchBookBean> { |
||||
private ConcurrentMultiValueMap<SearchBookBean, Book> mBooks; |
||||
|
||||
private ArrayList<SearchBookBean> mSearchBookBeans; |
||||
|
||||
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; |
||||
public SearchBookAdapter(ConcurrentMultiValueMap<SearchBookBean, Book> mBooks) { |
||||
this.mBooks = mBooks; |
||||
} |
||||
|
||||
@Override |
||||
public int getCount() { |
||||
return mSearchBookBeans.size(); |
||||
} |
||||
|
||||
@Override |
||||
public SearchBookBean getItem(int position) { |
||||
return mSearchBookBeans.get(position); |
||||
} |
||||
|
||||
@Override |
||||
public long getItemId(int position) { |
||||
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; |
||||
protected IViewHolder<SearchBookBean> createViewHolder(int viewType) { |
||||
return new SearchBookHolder(mBooks); |
||||
} |
||||
|
||||
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); |
||||
} |
||||
}*/ |
||||
|
||||
if (StringHelper.isEmpty(book.getImgUrl())){ |
||||
book.setImgUrl(""); |
||||
} |
||||
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)); |
||||
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; |
||||
} |
||||
} |
||||
|
||||
if (!hasSame) { |
||||
searchBookBeansAdd.add(temp); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onError(Exception e) { |
||||
|
||||
//添加
|
||||
for (SearchBookBean temp : searchBookBeansAdd) { |
||||
if (TextUtils.equals(keyWord, temp.getName())) { |
||||
for (int i = 0; i < copyDataS.size(); i++) { |
||||
SearchBookBean searchBook = copyDataS.get(i); |
||||
if (!TextUtils.equals(keyWord, searchBook.getName())) { |
||||
copyDataS.add(i, temp); |
||||
break; |
||||
} |
||||
} |
||||
} else if (TextUtils.equals(keyWord, temp.getAuthor())) { |
||||
for (int i = 0; i < copyDataS.size(); i++) { |
||||
SearchBookBean searchBook = copyDataS.get(i); |
||||
if (!TextUtils.equals(keyWord, searchBook.getName()) && !TextUtils.equals(keyWord, searchBook.getAuthor())) { |
||||
copyDataS.add(i, temp); |
||||
break; |
||||
} |
||||
} |
||||
} else { |
||||
copyDataS.add(temp); |
||||
} |
||||
} |
||||
}); |
||||
}else { |
||||
initOtherInfo(position, viewHolder); |
||||
} |
||||
//viewHolder.tvNewestChapter.setText(book.getNewestChapterTitle());
|
||||
//添加已经加载的书籍
|
||||
/*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); |
||||
} |
||||
MyApplication.runOnUiThread(this::notifyDataSetChanged); |
||||
} |
||||
//简介
|
||||
holder.tvDesc.setText("简介:" + book.getDesc()); |
||||
holder.tvType.setText(book.getType()); |
||||
} |
||||
|
||||
@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.content.Context; |
||||
import android.os.Handler; |
||||
import android.os.Message; |
||||
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.webapi.crawler.*; |
||||
import xyz.fycz.myreader.entity.SearchBookBean; |
||||
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.crawler.base.BookInfoCrawler; |
||||
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; |
||||
|
||||
import java.io.UnsupportedEncodingException; |
||||
import java.net.URLEncoder; |
@ -1,4 +1,4 @@ |
||||
package xyz.fycz.myreader.creator; |
||||
package xyz.fycz.myreader.ui.dialog; |
||||
|
||||
import android.content.Context; |
||||
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; |
||||
|
@ -1,4 +1,4 @@ |
||||
package xyz.fycz.myreader.callback; |
||||
package xyz.fycz.myreader.webapi.callback; |
||||
|
||||
|
||||
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. |
@ -1,4 +1,4 @@ |
||||
package xyz.fycz.myreader.webapi.crawler; |
||||
package xyz.fycz.myreader.webapi.crawler.base; |
||||
|
||||
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.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