/****************************************************************************************
* Copyright (c) 2014 Michael Goldbach <michael@m-goldbach.net> *
* *
* This program is free software; you can redistribute it and/or modify it under *
* the terms of the GNU General Public License as published by the Free Software *
* Foundation; either version 3 of the License, or (at your option) any later *
* version. *
* *
* This program is distributed in the hope that it will be useful, but WITHOUT ANY *
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
* PARTICULAR PURPOSE. See the GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License along with *
* this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/
package com.ichi2.anki.stats;
import android.content.res.Resources;
import android.database.Cursor;
import android.os.AsyncTask;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.webkit.WebView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.ichi2.anki.R;
import com.ichi2.anki.Statistics;
import com.ichi2.libanki.Collection;
import com.ichi2.libanki.Stats;
import com.ichi2.themes.Themes;
import com.wildplot.android.rendering.PlotSheet;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import timber.log.Timber;
public class AnkiStatsTaskHandler {
private static AnkiStatsTaskHandler sInstance;
private Collection mCollectionData;
private float mStandardTextSize = 10f;
private Stats.AxisType mStatType = Stats.AxisType.TYPE_MONTH;
private boolean mIsWholeCollection = false;
private static Lock sLock = new ReentrantLock();
public AnkiStatsTaskHandler(Collection collection){
sInstance = this;
mCollectionData = collection;
}
public void setIsWholeCollection(boolean wholeCollection) {
mIsWholeCollection = wholeCollection;
}
public static AnkiStatsTaskHandler getInstance() {
return sInstance;
}
public CreateChartTask createChart(Stats.ChartType chartType, View... views){
CreateChartTask createChartTask = new CreateChartTask(chartType);
createChartTask.execute(views);
return createChartTask;
}
public CreateStatisticsOverview createStatisticsOverview(View... views){
CreateStatisticsOverview createChartTask = new CreateStatisticsOverview();
createChartTask.execute(views);
return createChartTask;
}
public static DeckPreviewStatistics createReviewSummaryStatistics(Collection col, TextView view){
DeckPreviewStatistics deckPreviewStatistics = new DeckPreviewStatistics();
deckPreviewStatistics.execute(col, view);
return deckPreviewStatistics;
}
private class CreateChartTask extends AsyncTask<View, Void, PlotSheet>{
private ChartView mImageView;
private ProgressBar mProgressBar;
private boolean mIsRunning = false;
private Stats.ChartType mChartType;
public CreateChartTask(Stats.ChartType chartType){
super();
mIsRunning = true;
mChartType = chartType;
}
@Override
protected PlotSheet doInBackground(View... params) {
//make sure only one task of CreateChartTask is running, first to run should get sLock
//only necessary on lower APIs because after honeycomb only one thread is used for all asynctasks
sLock.lock();
try {
if (!mIsRunning) {
Timber.d("Quitting CreateChartTask (%s) before execution", mChartType.name());
return null;
} else {
Timber.d("Starting CreateChartTask, type: %s", mChartType.name());
}
mImageView = (ChartView) params[0];
mProgressBar = (ProgressBar) params[1];
ChartBuilder chartBuilder = new ChartBuilder(mImageView, mCollectionData,
mIsWholeCollection, mChartType);
return chartBuilder.renderChart(mStatType);
} finally {
sLock.unlock();
}
}
@Override
protected void onCancelled() {
mIsRunning = false;
}
@Override
protected void onPostExecute(PlotSheet plotSheet) {
if (plotSheet != null && mIsRunning) {
mImageView.setData(plotSheet);
mProgressBar.setVisibility(View.GONE);
mImageView.setVisibility(View.VISIBLE);
mImageView.invalidate();
}
}
}
private class CreateStatisticsOverview extends AsyncTask<View, Void, String>{
private WebView mWebView;
private ProgressBar mProgressBar;
private boolean mIsRunning = false;
public CreateStatisticsOverview(){
super();
mIsRunning = true;
}
@Override
protected String doInBackground(View... params) {
//make sure only one task of CreateChartTask is running, first to run should get sLock
//only necessary on lower APIs because after honeycomb only one thread is used for all asynctasks
sLock.lock();
try {
if (!mIsRunning) {
Timber.d("Quitting CreateStatisticsOverview before execution");
return null;
} else {
Timber.d("Starting CreateStatisticsOverview");
}
mWebView = (WebView) params[0];
mProgressBar = (ProgressBar) params[1];
OverviewStatsBuilder overviewStatsBuilder = new OverviewStatsBuilder(mWebView, mCollectionData, mIsWholeCollection, mStatType);
return overviewStatsBuilder.createInfoHtmlString();
} finally {
sLock.unlock();
}
}
@Override
protected void onCancelled() {
mIsRunning = false;
}
@Override
protected void onPostExecute(String html) {
if (html != null && mIsRunning) {
try {
mWebView.loadData(URLEncoder.encode(html, "UTF-8").replaceAll("\\+", " "), "text/html; charset=utf-8", "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
mProgressBar.setVisibility(View.GONE);
int backgroundColor = Themes.getColorFromAttr(mWebView.getContext(), android.R.attr.colorBackground);
mWebView.setBackgroundColor(backgroundColor);
mWebView.setVisibility(View.VISIBLE);
mWebView.invalidate();
}
}
}
private static class DeckPreviewStatistics extends AsyncTask<Object, Void, String> {
private TextView mTextView;
private boolean mIsRunning = false;
public DeckPreviewStatistics() {
super();
mIsRunning = true;
}
@Override
protected String doInBackground(Object... params) {
//make sure only one task of CreateChartTask is running, first to run should get sLock
//only necessary on lower APIs because after honeycomb only one thread is used for all asynctasks
sLock.lock();
try {
Collection collection = (Collection) params[0];
if (!mIsRunning || collection == null || collection.getDb() == null) {
Timber.d("Quitting DeckPreviewStatistics before execution");
return null;
} else {
Timber.d("Starting DeckPreviewStatistics");
}
mTextView = (TextView) params[1];
//eventually put this in Stats (in desktop it is not though)
int cards;
int minutes;
Cursor cur = null;
String query = "select count(), sum(time)/1000 from revlog where id > " + ((collection.getSched().getDayCutoff() - 86400) * 1000);
Timber.d("DeckPreviewStatistics query: " + query);
try {
cur = collection.getDb()
.getDatabase()
.rawQuery(query, null);
cur.moveToFirst();
cards = cur.getInt(0);
minutes = (int) Math.round(cur.getInt(1) / 60.0);
} finally {
if (cur != null && !cur.isClosed()) {
cur.close();
}
}
Resources res = collection.getContext().getResources();
final String span = res.getQuantityString(R.plurals.time_span_minutes, minutes, minutes);
return res.getQuantityString(R.plurals.studied_cards_today, cards, cards, span);
} finally {
sLock.unlock();
}
}
@Override
protected void onCancelled() {
mIsRunning = false;
}
@Override
protected void onPostExecute(String todayStatString) {
if (todayStatString != null && mIsRunning) {
mTextView.setText(todayStatString);
mTextView.setVisibility(View.VISIBLE);
mTextView.invalidate();
}
}
}
public float getmStandardTextSize() {
return mStandardTextSize;
}
public void setmStandardTextSize(float mStandardTextSize) {
this.mStandardTextSize = mStandardTextSize;
}
public Stats.AxisType getStatType() {
return mStatType;
}
public void setStatType(Stats.AxisType mStatType) {
this.mStatType = mStatType;
}
}