diff --git a/zhuike/src/main/java/com/novelbook/android/BookActivity.java b/zhuike/src/main/java/com/novelbook/android/BookActivity.java index ed5f103..4e086c4 100644 --- a/zhuike/src/main/java/com/novelbook/android/BookActivity.java +++ b/zhuike/src/main/java/com/novelbook/android/BookActivity.java @@ -4,6 +4,7 @@ import android.Manifest; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.os.Build; import android.os.Handler; import android.os.Message; @@ -24,6 +25,7 @@ import com.google.gson.Gson; import com.novelbook.android.adapter.BookListAdapter; import com.novelbook.android.bean.NovelSites; import com.novelbook.android.db.Chapter; +import com.novelbook.android.db.DownloadTask; import com.novelbook.android.db.Novel; import com.novelbook.android.filechooser.FileChooserActivity; import com.novelbook.android.netsubscribe.BookSubscribe; @@ -31,6 +33,8 @@ import com.novelbook.android.netsubscribe.MovieSubscribe; import com.novelbook.android.netutils.HttpMethods; import com.novelbook.android.netutils.OnSuccessAndFaultListener; import com.novelbook.android.netutils.OnSuccessAndFaultSub; + +import com.novelbook.android.service.ServiceDownload; import com.novelbook.android.utils.BookUtil; import com.novelbook.android.utils.GsonUtil; import com.novelbook.android.utils.MyImageLoader; @@ -217,6 +221,14 @@ public class BookActivity extends Activity_base { if (msg.what == 1 ) { setBookDetailInfo(); + }else if(msg.what==2) //准备数据,启动service + { + hideProgress(); + + + Toast.makeText(BookActivity.this,"已加入下载队列1",Toast.LENGTH_LONG).show(); + }else if(msg.what==3){ + Toast.makeText(BookActivity.this,"获取目录信息失败,下载失败",Toast.LENGTH_LONG).show(); } } @@ -235,20 +247,62 @@ public class BookActivity extends Activity_base { } - private void cacheBook() { - MovieSubscribe.getData(1,10,new OnSuccessAndFaultSub(new OnSuccessAndFaultListener() { - @Override - public void onSuccess(String result) { - //成功 - Toast.makeText(BookActivity.this,"请求成功:"+result,Toast.LENGTH_SHORT).show(); - } + + private void cacheBook() { + + if( LitePal.where("novelId = ?",mNovel.getId()+"").count("DownloadTask") >0){ + Toast.makeText(this,"已经在下载队列",Toast.LENGTH_LONG).show(); + Intent serviceIntent = new Intent(BookActivity.this, ServiceDownload.class); + startService(serviceIntent); + + return; + } + + + new Thread() { @Override - public void onFault(String errorMsg) { - //失败 - Toast.makeText(BookActivity.this,"请求失败:"+errorMsg,Toast.LENGTH_SHORT).show(); + public void run() { + int slepttime =0; + while (slepttime < 100 && pageFactory.getChapters().size() ==0){ + try { + sleep(50); + slepttime++; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if(pageFactory.getChapters().size() ==0){ + + handler.sendEmptyMessage(3); + + } + else{ + DownloadTask dt = new DownloadTask(); + dt.setDomain(mNovel.getDomain()); + dt.setFinishedChpats(0); + dt.setNovelId(mNovel.getId()); + dt.setStatus(0); + dt.setTotalChapts(mNovel.getTotalChapts()); + dt.save(); + // LitePal.deleteAll("chapter","novelId=?",mNovel.getId()+""); + for(Chapter chapter: pageFactory.getChapters()){ + chapter.setNovelId(mNovel.getId()); + chapter.save(); + } + Intent serviceIntent = new Intent(BookActivity.this, ServiceDownload.class); + serviceIntent.putExtra("taskId",dt.getId()); + startService(serviceIntent); + handler.sendEmptyMessage(2); + } + + // Toast.makeText(BookActivity.this,"已加入下载队列2",Toast.LENGTH_LONG).show(); } - },BookActivity.this)); + }.start(); + + + showProgress(false,"正在加入到队列,请等待"); + } void openBook(Novel book ) { diff --git a/zhuike/src/main/java/com/novelbook/android/ReadActivity.java b/zhuike/src/main/java/com/novelbook/android/ReadActivity.java index b941de5..0dcde38 100644 --- a/zhuike/src/main/java/com/novelbook/android/ReadActivity.java +++ b/zhuike/src/main/java/com/novelbook/android/ReadActivity.java @@ -770,15 +770,17 @@ public class ReadActivity extends Activity_base implements SpeechSynthesizerLis case R.id.llTopAd: Toast.makeText(this,"ad is clicked ",Toast.LENGTH_LONG).show(); Log.d(TAG,"Ad is clicked"); - showProgressDialog();// sleep 结束后才显示,dismiss 不工作 + + // showProgressDialog();// sleep 结束后才显示,dismiss 不工作 /* try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }*/ - dismissProgressDialog(); + // dismissProgressDialog(); break; } + hideSystemUI(); } ProgressDialog progressDialog; private void showProgressDialog() { diff --git a/zhuike/src/main/java/com/novelbook/android/db/Chapter.java b/zhuike/src/main/java/com/novelbook/android/db/Chapter.java index e27913d..1151def 100644 --- a/zhuike/src/main/java/com/novelbook/android/db/Chapter.java +++ b/zhuike/src/main/java/com/novelbook/android/db/Chapter.java @@ -1,9 +1,11 @@ package com.novelbook.android.db; +import org.litepal.LitePal; import org.litepal.crud.LitePalSupport; import java.io.Serializable; +import java.util.List; public class Chapter extends LitePalSupport implements Serializable { @@ -16,6 +18,15 @@ public class Chapter extends LitePalSupport implements Serializable { private int length; private String chapterPath; //缓存地址 private String domain; //目标 site + private int index;//第几章 + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } public String getDomain() { return domain; @@ -89,6 +100,11 @@ public class Chapter extends LitePalSupport implements Serializable { this.chapterPath = chapterPath; } + public static List getUnCachedChapters(int noveId,String domain ){ + // return LitePal.where("novelId = ? and domain = ? and chapterPath = ?" ,noveId+"",domain,"null") .find(Chapter.class); + return LitePal.where("novelId = ? and domain = ? " ,noveId+"",domain).find(Chapter.class); + + } } diff --git a/zhuike/src/main/java/com/novelbook/android/db/DownloadTask.java b/zhuike/src/main/java/com/novelbook/android/db/DownloadTask.java new file mode 100644 index 0000000..9b8c267 --- /dev/null +++ b/zhuike/src/main/java/com/novelbook/android/db/DownloadTask.java @@ -0,0 +1,66 @@ +package com.novelbook.android.db; + +import org.litepal.crud.LitePalSupport; + +import java.io.Serializable; + +public class DownloadTask extends LitePalSupport implements Serializable { + + private int id; + private int novelId; + private String domain; + private int status; + private int totalChapts; + private int finishedChpats; + + public int getTotalChapts() { + return totalChapts; + } + + public void setTotalChapts(int totalChapts) { + this.totalChapts = totalChapts; + } + + public int getFinishedChpats() { + return finishedChpats; + } + + public void setFinishedChpats(int finishedChpats) { + this.finishedChpats = finishedChpats; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getNovelId() { + return novelId; + } + + public void setNovelId(int novelId) { + this.novelId = novelId; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + +} + diff --git a/zhuike/src/main/java/com/novelbook/android/db/Novel.java b/zhuike/src/main/java/com/novelbook/android/db/Novel.java index 0c220e1..108c2de 100644 --- a/zhuike/src/main/java/com/novelbook/android/db/Novel.java +++ b/zhuike/src/main/java/com/novelbook/android/db/Novel.java @@ -33,6 +33,16 @@ public class Novel extends LitePalSupport implements Serializable{ private boolean isOnShelf; //是否入书架 private boolean isFinished; //是否完本 + public int getTotalChapts() { + return totalChapts; + } + + public void setTotalChapts(int totalChapts) { + this.totalChapts = totalChapts; + } + + private int totalChapts=1000; + public int getId() { diff --git a/zhuike/src/main/java/com/novelbook/android/db/SiteRule.java b/zhuike/src/main/java/com/novelbook/android/db/SiteRule.java index 7fc68c7..294e666 100644 --- a/zhuike/src/main/java/com/novelbook/android/db/SiteRule.java +++ b/zhuike/src/main/java/com/novelbook/android/db/SiteRule.java @@ -1,8 +1,11 @@ package com.novelbook.android.db; +import org.litepal.LitePal; import org.litepal.annotation.Column; import org.litepal.crud.LitePalSupport; +import java.util.List; + public class SiteRule extends LitePalSupport { private int id; private String name; @@ -95,4 +98,10 @@ public class SiteRule extends LitePalSupport { public void setHeaders(String[] headers) { this.headers = headers; } + + public static SiteRule getSiteRuleByDomain(String domain){ + List rules = LitePal.where("domain = ?",domain).limit(1).find(SiteRule.class); + SiteRule sr = rules.size()> 0 ? rules.get(0): null; + return sr; + } } diff --git a/zhuike/src/main/java/com/novelbook/android/service/ChapterDownloadSvrc.java b/zhuike/src/main/java/com/novelbook/android/service/ChapterDownloadSvrc.java new file mode 100644 index 0000000..6e777c4 --- /dev/null +++ b/zhuike/src/main/java/com/novelbook/android/service/ChapterDownloadSvrc.java @@ -0,0 +1,25 @@ +package com.novelbook.android.service; + +import android.app.IntentService; +import android.content.Intent; + +public class ChapterDownloadSvrc extends IntentService { + public ChapterDownloadSvrc(String name) { + super(name); + } + + @Override + public void onCreate() { + super.onCreate(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + @Override + protected void onHandleIntent(Intent intent) { + + } +} diff --git a/zhuike/src/main/java/com/novelbook/android/service/ServiceDownload.java b/zhuike/src/main/java/com/novelbook/android/service/ServiceDownload.java new file mode 100644 index 0000000..1ea091e --- /dev/null +++ b/zhuike/src/main/java/com/novelbook/android/service/ServiceDownload.java @@ -0,0 +1,320 @@ +package com.novelbook.android.service; + +import android.app.IntentService; +import android.content.Intent; +import android.os.Handler; +import android.os.Message; +import android.text.TextUtils; +import android.util.Log; + +import com.novelbook.android.MyApp; +import com.novelbook.android.db.Chapter; +import com.novelbook.android.db.DownloadTask; +import com.novelbook.android.db.SiteRule; +import com.novelbook.android.netutils.HttpMethods; +import com.novelbook.android.utils.BookUtil; +import com.novelbook.android.utils.FileUtils; +import com.novelbook.android.utils.NovelParseUtil; + +import org.json.JSONException; +import org.json.JSONObject; +import org.litepal.LitePal; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.spi.CharsetProvider; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import java.util.logging.LogRecord; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + + +public class ServiceDownload extends IntentService { + private static final String TAG ="ServiceDownload" ; + + private final String storagePath = BookUtil.storagePath; + private final String cachedPath = BookUtil.cachedPath; + private final String chapterPath = BookUtil.chapterPath; + + + + /** + * Creates an IntentService. Invoked by your subclass's constructor. + * + * @param name Used to name the worker thread, important only for debugging. + */ + + public ServiceDownload(String name) { + super(name); + } + public ServiceDownload() { + super(TAG); + } + + @Override + public void onCreate() { + super.onCreate(); + + + downloadTasks =LitePal.where("status = ?","0").find(DownloadTask.class); + for(DownloadTask dt :downloadTasks){ + List chps =Chapter.getUnCachedChapters(dt.getNovelId(),dt.getDomain()); + if(chps.size()>0) { + SiteRule sr = SiteRule.getSiteRuleByDomain(dt.getDomain()); + siteRuleMap.put(dt.getNovelId(), sr); + tasksMap.put(dt.getId(), chps); + } + } + + new Thread() { + + @Override + public void run() { + + + } + + }.start(); + + + + } + + + // key = taskId + private Map> tasksMap = new HashMap>(); + + private List downloadTasks = new ArrayList(); + + @Override + protected void onHandleIntent( Intent intent) { + String key ="taskId"; + int taskId =0; + if(intent.hasExtra(key)){ + taskId= intent.getExtras().getInt(key); + startNewTask(taskId); + }else{ + startTask(); + } + + } + Handler handler = new Handler() { + @Override + public void handleMessage(Message msg) { + if(msg.what ==1){ + startTask(); + }else if(msg.what==2){ + Log.d(TAG,String.format("%s start new chapt download---- taskId :%s, chapter count %s,chaptIndex %s",TAG + , processingTask.getId(), + tasksMap.get(processingTask.getId()).size(),chaptIndex )); + + if( tasksMap.get(processingTask.getId()).size()-1 > chaptIndex){ + chaptIndex++; + }else{ + //章节全部处理完毕了 + processingTask.setStatus(1); + processingTask.update(processingTask.getId()); + + startTask(); + } + + doDownloadContent(); + } + + } + + }; + + // key = novelId + Map siteRuleMap = new HashMap() ; ; + DownloadTask processingTask ; + int taskIndex=0; + private void startTask() { + if(downloadTasks.size() <=taskIndex){ + + Log.d(TAG,"No task to download."); + return; + } + + + processingTask = downloadTasks.get(taskIndex); + chaptIndex=0; + doDownloadContent(); + if(taskIndex < downloadTasks.size()-1){ + taskIndex++; + } + + } + + int chaptIndex=0; + void doDownloadContent(){ + + Log.d(TAG, String.format("ServiceDowload--- taskId: %s ",processingTask.getId())); + List chps = tasksMap.get(processingTask.getId()); + if(chps!=null && chps.size()>0) { + Chapter chapter = chps.get(chaptIndex); + + if (TextUtils.isEmpty(chapter.getChapterPath())) { + try { + ServiceDownload(chapter); + } catch (JSONException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } else { + handler.sendEmptyMessage(2); + } + }else{ + handler.sendEmptyMessage(2); + } + } + + synchronized void startNewTask(int taskId){ + + new Thread() { + + @Override + public void run() { + + DownloadTask dt = LitePal.find(DownloadTask.class,taskId); + if(dt!=null){ + + SiteRule siteRule = SiteRule. getSiteRuleByDomain(dt.getDomain()); + if(siteRule==null){ + //to do get siterule from web + }else { + siteRuleMap.put(dt.getNovelId(),siteRule); + List chps = Chapter.getUnCachedChapters(dt.getNovelId(),dt.getDomain() ); + downloadTasks.add(dt); + tasksMap.put(dt.getId(), chps); + if(taskIndex==0) { + handler.sendEmptyMessage(1); + } + } + } + + } + }.start(); + + } + + private void ServiceDownload( Chapter chapter ) throws JSONException, InterruptedException { + + int msg =2; + + String url = chapter.getChapterUrl(); + if( TextUtils.isEmpty( url)){ + handler.sendEmptyMessage(msg); + return ; + } + long startTime= new Date().getTime(); + Log.d(TAG,String.format("ServiceDowload----start download %s from %s", chapter.getChapterName() ,url )); + + + + // Log.d( "ServiceDowload",String.format("ServiceDowload isDownloadChapt: %s",isDownloadChapt)); + JSONObject siteJson = new JSONObject(); + siteJson.put("chapterContentRegex", siteRuleMap.get(chapter.getNovelId()).getChapterContentRegex()); + siteJson.put("chapterContentDumpRegex", siteRuleMap.get(chapter.getNovelId()).getChapterContentDumpRegex()); + Request request = getTagRequest(url); + HttpMethods.getOkClient().newCall(request).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + + handler.sendEmptyMessage(msg); + + // Log.d( "ServiceDowload",String.format("ServiceDowload fail, isDownloadChapt: %s",isDownloadChapt)); + e.printStackTrace(); + // throw new RuntimeException("Error during writing " + fileChapterName( index)); + } + + @Override + public void onResponse(Call call, Response response){ + ResponseBody body = response.body(); + if (body != null ) { + if(response.code()!=200){ + Log.d(TAG, "ServiceDowload----network failure returnCode " + response.code()); + // setDownloadFlag(true); + // chaptDownStatus.put(index, BookUtil.DownloadStatus.failure); + // Log.d( "ServiceDowload",String.format("ServiceDowload error %s ,isDownloadChapt: %s", response.code(),isDownloadChapt)); + handler.sendEmptyMessage(msg); + return; + } + + try { + String bodyStr = body.string(); + String title = chapter.getChapterName(); + String chapterContent = title+ "\n" + NovelParseUtil.getChapterContent(bodyStr, siteJson); + char[] buf = chapterContent.toCharArray(); + File file = new File(fileChapterName(chapter)); + file.createNewFile(); + + final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileChapterName(chapter)), "utf-8");//"UTF-16LE"); // UTF-16LE 比 utf-8 文件小 + writer.write(buf); + writer.close(); + Log.d( "ServiceDowload",String.format("ServiceDowload file created: %s", file.getPath())); + + Thread.sleep(siteRuleMap.get(chapter.getNovelId()).getMiniInterval4AccessChapter()); + // setDownloadFlag(true); + } catch (IOException | JSONException | InterruptedException e) { + e.printStackTrace(); + // throw new RuntimeException("Error during writing " + fileChapterName( index)); + } + finally { + body.close(); + + + // setDownloadFlag(true); + } + + chapter.setChapterPath(fileChapterName(chapter)); + chapter.update(chapter.getId()); + handler.sendEmptyMessage(msg); + + //setDownloadFlag(true); + // chaptDownStatus.put(index, BookUtil.DownloadStatus.success); + Log.d(TAG,String.format("ServiceDowload---- finished download %s, cost time %s ,content path %s ", chapter.getChapterName(), new Date().getTime() -startTime ,chapter.getChapterPath() )); + + } + } + }); + + + } + + protected String fileChapterName(Chapter chapter ) { + return getChapterPath(chapter.getNovelId()) +chapter.getDomain()+"/"+ chapter.getIndex() ; + + } + String getChapterPath(int novelId){ + File file = new File(chapterPath +novelId); + if(!file.exists()){ + file.mkdir(); + } + return chapterPath +novelId+"/"; + } + + private Request getTagRequest(String url) { + return new Request.Builder() + .tag(processingTask.getNovelId()) //标记 + .url(url) + // .header("User-Agent", "OkHttp Example") + .build(); + } + + + +} diff --git a/zhuike/src/main/java/com/novelbook/android/utils/BookUtil.java b/zhuike/src/main/java/com/novelbook/android/utils/BookUtil.java index 45af735..abc4227 100644 --- a/zhuike/src/main/java/com/novelbook/android/utils/BookUtil.java +++ b/zhuike/src/main/java/com/novelbook/android/utils/BookUtil.java @@ -54,9 +54,9 @@ import okhttp3.ResponseBody; public class BookUtil { public static final String TAG ="BookUtil"; - private static final String storagePath = FileUtils.getDiskCacheDir(MyApp.applicationContext);//Environment.getExternalStorageDirectory() + "/zhuike"; - private static final String cachedPath = storagePath + "/cache/"; - private static final String chapterPath = storagePath + "/chapter/"; + public static final String storagePath = FileUtils.getDiskCacheDir(MyApp.applicationContext);//Environment.getExternalStorageDirectory() + "/zhuike"; + public static final String cachedPath = storagePath + "/cache/"; + public static final String chapterPath = storagePath + "/chapter/"; private static final String charachterType = "utf-8";//"UTF-16LE"; private Context mContext; private ProgressDialog progressDialog; @@ -834,7 +834,14 @@ public class BookUtil { }; private Map chaptCache = new HashMap(); - + private Map chaptDownStatus = new HashMap(); + DownloadStatus downloadStatus = DownloadStatus.notStart; + private enum DownloadStatus{ + notStart, + downloading, + failure, + success + } //获取chapter 缓存 public char[] chaptChars(int index) { char[] block=null; @@ -860,7 +867,8 @@ public class BookUtil { Log.d( "loadChaptContent",String.format("begin to load content for chapter %s",index)); Log.d( "loadChaptContent",String.format("isDownloadChapt: %s",isDownloadChapt)); - if(getDownloadStatus() ) { + if(!chaptDownStatus.containsKey(Integer.valueOf(index))){ + chaptDownStatus.put(index,DownloadStatus.downloading); loadChaptContent(index); } @@ -869,10 +877,11 @@ public class BookUtil { int maxSleep =6000; int slepttime =0; // while(!file.exists() && !getDownloadStatus()){//&& slepttime index ) { @@ -957,6 +966,7 @@ private void loadChaptContent(int index) throws JSONException, InterruptedExcept public void onFailure(Call call, IOException e) { handler.sendEmptyMessage(123); handler.sendEmptyMessage(1); + chaptDownStatus.put(index,DownloadStatus.failure); setDownloadFlag(true); Log.d( "loadChaptContent",String.format("loadChaptContent fail, isDownloadChapt: %s",isDownloadChapt)); e.printStackTrace(); @@ -970,6 +980,7 @@ private void loadChaptContent(int index) throws JSONException, InterruptedExcept if(response.code()!=200){ Log.d(TAG, "loadChaptContent----network failure returnCode " + response.code()); setDownloadFlag(true); + chaptDownStatus.put(index,DownloadStatus.failure); Log.d( "loadChaptContent",String.format("loadChaptContent error %s ,isDownloadChapt: %s", response.code(),isDownloadChapt)); handler.sendEmptyMessage(1); return; @@ -1003,6 +1014,7 @@ private void loadChaptContent(int index) throws JSONException, InterruptedExcept chapter.setChapterPath(fileChapterName(index)); chapter.save(); setDownloadFlag(true); + chaptDownStatus.put(index,DownloadStatus.success); Log.d(TAG,String.format("loadChaptContent---- finished download %s, cost time %s ,content path %s ", chapter.getChapterName(), new Date().getTime() -startTime ,chapter.getChapterPath() )); } diff --git a/zhuike/src/main/java/com/novelbook/android/utils/NovelParseUtil.java b/zhuike/src/main/java/com/novelbook/android/utils/NovelParseUtil.java index 17aedef..8b6cbac 100644 --- a/zhuike/src/main/java/com/novelbook/android/utils/NovelParseUtil.java +++ b/zhuike/src/main/java/com/novelbook/android/utils/NovelParseUtil.java @@ -40,10 +40,13 @@ public class NovelParseUtil { Set> es = muluMap.entrySet(); int pos = tmp.length - 1; for (Map.Entry e : es) { + Chapter chapter = new Chapter(); chapter.setChapterUrl( e.getKey()); chapter.setChapterName( e.getValue()); chapter.setDomain(domain); + chapter.setIndex(pos+1); //第几章 + tmp[pos--] =chapter; } List values = new ArrayList(Arrays.asList(tmp)); diff --git a/zhuike/src/main/java/com/novelbook/android/utils/PageFactory.java b/zhuike/src/main/java/com/novelbook/android/utils/PageFactory.java index a3741b0..c49defe 100644 --- a/zhuike/src/main/java/com/novelbook/android/utils/PageFactory.java +++ b/zhuike/src/main/java/com/novelbook/android/utils/PageFactory.java @@ -568,9 +568,9 @@ public class PageFactory { * @throws IOException */ public void openBook(Novel book ,Context context) throws IOException { - if(book.isLocalBook() ){ //离线书籍重新初始化加载mBookUtil - mBookUtil = new BookUtil(); + if(book.isLocalBook() || mBookUtil==null){ //离线书籍重新初始化加载mBookUtil + mBookUtil = new BookUtil(); } mBookUtil.setContext(context); //清空数据