package com.erakk.lnreader.task;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
import com.erakk.lnreader.Constants;
import com.erakk.lnreader.LNReaderApplication;
import com.erakk.lnreader.callback.CallbackEventData;
import com.erakk.lnreader.callback.ICallbackEventData;
import com.erakk.lnreader.callback.ICallbackNotifier;
import com.erakk.lnreader.callback.IExtendedCallbackNotifier;
import com.erakk.lnreader.dao.NovelsDao;
import com.erakk.lnreader.helper.BakaReaderException;
import com.erakk.lnreader.model.NovelCollectionModel;
import com.erakk.lnreader.model.PageModel;
import com.erakk.lnreader.service.UpdateScheduleReceiver;
import com.erakk.lnreader.service.UpdateService;
import java.util.ArrayList;
import java.util.Date;
public class GetUpdatedChaptersTask extends AsyncTask<Void, String, AsyncTaskResult<ArrayList<PageModel>>> implements ICallbackNotifier {
private static final String TAG = GetUpdatedChaptersTask.class.toString();
private int lastProgress;
private final boolean autoDownloadUpdatedContent;
private final UpdateService service;
private IExtendedCallbackNotifier<AsyncTaskResult<?>> notifier;
private String source;
private final ArrayList<Exception> exceptionList;
public GetUpdatedChaptersTask(UpdateService service, boolean autoDownloadUpdatedContent, IExtendedCallbackNotifier<AsyncTaskResult<?>> notifier) {
this.autoDownloadUpdatedContent = autoDownloadUpdatedContent;
this.service = service;
this.notifier = notifier;
Log.d(TAG, "Auto Download: " + autoDownloadUpdatedContent);
this.exceptionList = new ArrayList<Exception>();
}
@Override
protected AsyncTaskResult<ArrayList<PageModel>> doInBackground(Void... arg0) {
// add on Download List
LNReaderApplication.getInstance().addDownload(TAG, "Update Service");
service.setRunning(true);
try {
ArrayList<PageModel> result = getUpdatedChapters(this);
return new AsyncTaskResult<ArrayList<PageModel>>(result, result.getClass());
} catch (Exception ex) {
Log.e(TAG, "Error when updating", ex);
return new AsyncTaskResult<ArrayList<PageModel>>(null, ArrayList.class, ex);
}
}
@Override
protected void onPostExecute(AsyncTaskResult<ArrayList<PageModel>> result) {
Exception e = result.getError();
if (e == null) {
service.sendNotification(result.getResult());
} else {
String text = "Error when getting updates: " + e.getMessage();
Log.e(TAG, text, e);
service.updateStatus("ERROR==>" + e.getMessage());
Toast.makeText(service.getApplicationContext(), text, Toast.LENGTH_LONG).show();
}
// Reschedule for next run
UpdateScheduleReceiver.reschedule(service);
service.setRunning(false);
// update last run
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(service.getApplicationContext());
SharedPreferences.Editor editor = preferences.edit();
editor.putLong(Constants.PREF_LAST_UPDATE, new Date().getTime());
editor.commit();
// remove from download list
LNReaderApplication.getInstance().removeDownload(TAG);
}
/***
* Check ToS, Novel List, and Watched List.
*
* @param callback
* @return
* @throws Exception
*/
private ArrayList<PageModel> getUpdatedChapters(ICallbackNotifier callback) throws Exception {
Log.d(TAG, "Checking Updates...");
ArrayList<PageModel> updatesTotal = new ArrayList<PageModel>();
PageModel updatedTos = getUpdatedTOS(callback);
if (updatedTos != null) {
updatesTotal.add(updatedTos);
}
// check updated novel list
ArrayList<PageModel> updatedNovelList = getUpdatedNovelList(callback);
if (updatedNovelList != null && updatedNovelList.size() > 0) {
Log.d(TAG, "Got new novel! ");
for (PageModel pageModel : updatedNovelList) {
updatesTotal.add(pageModel);
}
}
// check only watched novel
ArrayList<PageModel> updatedWatchedNovelList = getUpdatedWatchedNovelList(callback);
if (updatedWatchedNovelList != null && updatedWatchedNovelList.size() > 0) {
Log.d(TAG, "Got update watched novels! ");
for (PageModel pageModel : updatedWatchedNovelList) {
updatesTotal.add(pageModel);
}
}
service.setForce(false);
Log.i(TAG, "Found updates: " + updatesTotal.size());
return updatesTotal;
}
/***
* Download individual chapter.
*
* @param chapter
* @param callback
*/
private void downloadUpdatedChapter(PageModel chapter, ICallbackNotifier callback) {
if (isCancelled())
return;
try {
String message = "Downloading updated content for: " + chapter.getPage();
Log.i(TAG, message);
NovelsDao dao = NovelsDao.getInstance();
if (callback != null) {
callback.onProgressCallback(new CallbackEventData(message, source));
}
if (!chapter.isMissing() && !chapter.isExternal()) {
dao.getNovelContentFromInternet(chapter, callback);
// try to remove the redlink page
if(!chapter.getPage().endsWith("redlink=1")) {
PageModel redChapter = new PageModel();
redChapter.setPage(chapter.getPage() + "&action=edit&redlink=1");
redChapter = dao.getExistingPageModel(redChapter, callback);
if(redChapter != null) {
dao.deletePage(redChapter);
Log.i(TAG, "Remove redlink chapter for: " + chapter.getPage());
}
}
}
} catch (Exception ex) {
String msg = "Failed to update chapter: " + chapter.getPage();
BakaReaderException bex = new BakaReaderException(msg, BakaReaderException.UPDATE_FAILED_CHAPTER, ex);
exceptionList.add(bex);
Log.e(TAG, msg, bex);
publishProgress(msg);
}
}
/***
* Get updated chapter for given watched novel.
*
* @param novel
* @param callback
* @return
*/
private ArrayList<PageModel> processWatchedNovel(PageModel novel, ICallbackNotifier callback) {
ArrayList<PageModel> updatedWatchedNovel = new ArrayList<PageModel>();
NovelsDao dao = NovelsDao.getInstance();
if (callback != null)
callback.onProgressCallback(new CallbackEventData("Checking: " + novel.getTitle(), source));
try {
// get last update date from internet
PageModel updatedNovel = dao.getPageModelFromInternet(novel.getPageModel(), callback);
// different timestamp
if (service.isForced() || novel.getLastUpdate().before(updatedNovel.getLastUpdate())) {
if (service.isForced()) {
Log.i(TAG, "Force Mode: " + novel.getPage());
} else {
Log.d(TAG, "Different Timestamp for: " + novel.getPage());
Log.d(TAG, "old: " + novel.getLastUpdate().toString() + " before " + updatedNovel.getLastUpdate().toString());
}
if (isCancelled())
return updatedWatchedNovel;
ArrayList<PageModel> novelDetailsChapters = dao.getNovelDetails(novel, callback, true).getFlattedChapterList();
if (callback != null)
callback.onProgressCallback(new CallbackEventData("Getting updated chapters: " + novel.getTitle(), source));
NovelCollectionModel updatedNovelDetails = dao.getNovelDetailsFromInternet(novel, callback);
if (updatedNovelDetails != null) {
ArrayList<PageModel> updates = updatedNovelDetails.getFlattedChapterList();
Log.d(TAG, "Starting size: " + updates.size());
// compare the chapters!
for (int i = 0; i < novelDetailsChapters.size(); ++i) {
if (isCancelled())
return updatedWatchedNovel;
PageModel oldChapter = novelDetailsChapters.get(i);
for (int j = 0; j < updates.size(); j++) {
PageModel newChapter = updates.get(j);
if (callback != null)
callback.onProgressCallback(new CallbackEventData("Checking: " + oldChapter.getTitle() + " ==> " + newChapter.getTitle(), source));
// check if the same page
if (newChapter.getPage().compareTo(oldChapter.getPage()) == 0) {
// check if last update date is newer
if (newChapter.getLastUpdate().getTime() > oldChapter.getLastUpdate().getTime()) {
newChapter.setUpdated(true);
Log.i(TAG, "Found updated chapter: " + newChapter.getTitle());
} else {
updates.remove(newChapter);
Log.i(TAG, "No Update for Chapter: " + newChapter.getTitle());
}
break;
}
}
}
Log.d(TAG, "End size: " + updates.size());
updatedWatchedNovel.addAll(updates);
}
}
} catch (Exception ex) {
String msg = "Failed to update: " + novel.getTitle();
BakaReaderException bex = new BakaReaderException(msg, BakaReaderException.UPDATE_FAILED_NOVEL, ex);
exceptionList.add(bex);
Log.e(TAG, msg, bex);
publishProgress(msg);
}
return updatedWatchedNovel;
}
/***
* Check Watched Novels List
*
* @param callback
* @return
*/
private ArrayList<PageModel> getUpdatedWatchedNovelList(ICallbackNotifier callback) {
if (isCancelled())
return null;
if (callback != null)
callback.onProgressCallback(new CallbackEventData("Getting watched novel.", source));
ArrayList<PageModel> newList = new ArrayList<PageModel>();
try {
ArrayList<PageModel> watchedNovels = NovelsDao.getInstance().getWatchedNovel();
if (watchedNovels != null) {
double total = watchedNovels.size() + 1;
double current = 0;
for (PageModel watchedNovel : watchedNovels) {
if (isCancelled())
break;
ArrayList<PageModel> updatedChapters = processWatchedNovel(watchedNovel, callback);
newList.addAll(updatedChapters);
if (autoDownloadUpdatedContent) {
for (PageModel chapter : updatedChapters) {
downloadUpdatedChapter(chapter, callback);
}
Log.i(TAG, "Updated Chapter Downloaded: " + updatedChapters.size() + " for: " + watchedNovel.getPage());
}
lastProgress = (int) (++current / total * 100);
Log.d(TAG, "Progress: " + lastProgress);
}
}
} catch (Exception ex) {
String msg = "Failed to update: Watched Novel.";
BakaReaderException bex = new BakaReaderException(msg, BakaReaderException.UPDATE_FAILED_WATCHED_NOVEL, ex);
exceptionList.add(bex);
Log.e(TAG, msg, bex);
publishProgress(msg);
}
return newList;
}
/***
* Check new/updated Novels from //www.baka-tsuki.org/project/index.php?title=Category:Light_novel_(English)
*
* @param callback
* @return
*/
private ArrayList<PageModel> getUpdatedNovelList(ICallbackNotifier callback) {
if (isCancelled())
return null;
ArrayList<PageModel> newList = null;
try {
PageModel mainPage = new PageModel();
mainPage.setPage(Constants.ROOT_NOVEL_ENGLISH);
mainPage = NovelsDao.getInstance().getPageModel(mainPage, callback);
// check if local main page's last check time is more than 7 day
Date today = new Date();
long diff = today.getTime() - mainPage.getLastCheck().getTime();
if (service.isForced() || diff > Constants.CHECK_INTERVAL && LNReaderApplication.getInstance().isOnline()) {
Log.d(TAG, "Last check is over 7 days, checking online status");
ArrayList<PageModel> currList = NovelsDao.getInstance().getNovels(callback, true);
newList = NovelsDao.getInstance().getNovelsFromInternet(callback);
for (int i = 0; i < currList.size(); ++i) {
for (int j = 0; j < newList.size(); ++j) {
if (currList.get(i).getPage().equalsIgnoreCase(newList.get(j).getPage())) {
newList.remove(j);
break;
}
}
}
}
} catch (Exception ex) {
String msg = "Failed to update: English Novel List.";
BakaReaderException bex = new BakaReaderException(msg, BakaReaderException.UPDATE_FAILED_NOVEL_ENGLISH, ex);
exceptionList.add(bex);
Log.e(TAG, msg, bex);
publishProgress(msg);
}
return newList;
}
/***
* Check Term of Service from //www.baka-tsuki.org/project/index.php?title=Baka-Tsuki:Copyrights
*
* @param callback
* @return
*/
private PageModel getUpdatedTOS(ICallbackNotifier callback) {
if (isCancelled())
return null;
try {
// checking copyrights
PageModel p = new PageModel();
p.setPage("Baka-Tsuki:Copyrights");
p.setTitle("Baka-Tsuki:Copyrights");
p.setType(PageModel.TYPE_TOS);
// get current tos
NovelsDao.getInstance().getPageModel(p, callback);
PageModel newP = NovelsDao.getInstance().getPageModelFromInternet(p, callback);
if (newP != null && newP.getLastUpdate().getTime() > p.getLastUpdate().getTime()) {
Log.d(TAG, "TOS Updated.");
return newP;
}
} catch (Exception ex) {
String msg = "Failed to update: Term of Service.";
BakaReaderException bex = new BakaReaderException(msg, BakaReaderException.UPDATE_FAILED_TOS, ex);
exceptionList.add(bex);
Log.e(TAG, msg, bex);
publishProgress(msg);
}
return null;
}
@Override
public void onProgressCallback(ICallbackEventData message) {
publishProgress(message.getMessage());
}
@Override
protected void onProgressUpdate(String... values) {
if (notifier != null)
notifier.onProgressCallback(new CallbackEventData(values[0], lastProgress, Constants.PREF_RUN_UPDATES));
LNReaderApplication.getInstance().updateDownload(TAG, lastProgress, values[0]);
}
public void setCallbackNotifier(IExtendedCallbackNotifier<AsyncTaskResult<?>> notifier) {
this.notifier = notifier;
}
}