/*
* ____.____ __.____ ___ _____
* | | |/ _| | \ / _ \ ______ ______
* | | < | | / / /_\ \\____ \\____ \
* /\__| | | \| | / / | \ |_> > |_> >
* \________|____|__ \______/ \____|__ / __/| __/
* \/ \/|__| |__|
*
* Copyright (c) 2014-2015 Paul "Marunjar" Pretsch
*
* 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 org.voidsink.anewjkuapp.update;
import android.accounts.Account;
import android.content.ContentProviderClient;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SyncResult;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import org.voidsink.anewjkuapp.KusssContentContract;
import org.voidsink.anewjkuapp.R;
import org.voidsink.anewjkuapp.analytics.Analytics;
import org.voidsink.anewjkuapp.kusss.Course;
import org.voidsink.anewjkuapp.kusss.KusssHandler;
import org.voidsink.anewjkuapp.kusss.KusssHelper;
import org.voidsink.anewjkuapp.kusss.Term;
import org.voidsink.anewjkuapp.notification.SyncNotification;
import org.voidsink.anewjkuapp.provider.KusssContentProvider;
import org.voidsink.anewjkuapp.utils.AppUtils;
import org.voidsink.anewjkuapp.utils.Consts;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
public class ImportCourseTask implements Callable<Void> {
private static final String TAG = ImportCourseTask.class.getSimpleName();
private ContentProviderClient mProvider;
private boolean mReleaseProvider = false;
private final Account mAccount;
private SyncResult mSyncResult;
private final Context mContext;
private final ContentResolver mResolver;
private boolean mShowProgress;
private SyncNotification mUpdateNotification;
public static final String[] COURSE_PROJECTION = new String[]{
KusssContentContract.Course.COL_ID,
KusssContentContract.Course.COL_TERM,
KusssContentContract.Course.COL_COURSEID,
KusssContentContract.Course.COL_TITLE,
KusssContentContract.Course.COL_CURRICULA_ID,
KusssContentContract.Course.COL_TYPE,
KusssContentContract.Course.COL_LECTURER,
KusssContentContract.Course.COL_SWS,
KusssContentContract.Course.COL_ECTS,
KusssContentContract.Course.COL_CLASS_CODE};
public static final int COLUMN_LVA_ID = 0;
public static final int COLUMN_LVA_TERM = 1;
public static final int COLUMN_LVA_COURSEID = 2;
public static final int COLUMN_LVA_TITLE = 3;
public static final int COLUMN_LVA_CURRICULA_ID = 4;
public static final int COLUMN_LVA_TYPE = 5;
public static final int COLUMN_LVA_TEACHER = 6;
public static final int COLUMN_LVA_SWS = 7;
public static final int COLUMN_LVA_ECTS = 8;
public static final int COLUMN_LVA_CODE = 9;
public ImportCourseTask(Account account, Context context) {
this(account, null, null, null, null, context);
this.mProvider = context.getContentResolver()
.acquireContentProviderClient(
KusssContentContract.Course.CONTENT_URI);
this.mReleaseProvider = true;
this.mSyncResult = new SyncResult();
this.mShowProgress = true;
}
public ImportCourseTask(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult,
Context context) {
this.mAccount = account;
this.mProvider = provider;
this.mSyncResult = syncResult;
this.mResolver = context.getContentResolver();
this.mContext = context;
this.mShowProgress = (extras != null && extras.getBoolean(Consts.SYNC_SHOW_PROGRESS, false));
}
private void updateNotify(String string) {
if (mUpdateNotification != null) {
mUpdateNotification.update(string);
}
}
@Override
public Void call() throws Exception {
if (mProvider == null) {
return null;
}
if (mShowProgress) {
mUpdateNotification = new SyncNotification(mContext,
R.string.notification_sync_lva);
mUpdateNotification.show(mContext.getString(R.string.notification_sync_lva_loading));
}
try {
Log.d(TAG, "setup connection");
updateNotify(mContext.getString(R.string.notification_sync_connect));
if (KusssHandler.getInstance().isAvailable(mContext,
AppUtils.getAccountAuthToken(mContext, mAccount),
AppUtils.getAccountName(mContext, mAccount),
AppUtils.getAccountPassword(mContext, mAccount))) {
updateNotify(mContext.getString(R.string.notification_sync_lva_loading));
Log.d(TAG, "load lvas");
List<Term> terms = KusssContentProvider.getTerms(mContext);
List<Course> courses = KusssHandler.getInstance().getLvas(mContext, terms);
if (courses == null) {
mSyncResult.stats.numParseExceptions++;
} else {
Map<String, Course> lvaMap = new HashMap<>();
for (Course course : courses) {
lvaMap.put(KusssHelper.getCourseKey(course.getTerm(), course.getCourseId()), course);
}
Map<String, Term> termMap = new HashMap<>();
for (Term term : terms) {
termMap.put(term.toString(), term);
}
Log.d(TAG, String.format("got %s lvas", courses.size()));
updateNotify(mContext.getString(R.string.notification_sync_lva_updating));
ArrayList<ContentProviderOperation> batch = new ArrayList<>();
Uri lvaUri = KusssContentContract.Course.CONTENT_URI;
Cursor c = mProvider.query(lvaUri, COURSE_PROJECTION,
null, null, null);
if (c == null) {
Log.w(TAG, "selection failed");
} else {
Log.d(TAG,
"Found "
+ c.getCount()
+ " local entries. Computing merge solution...");
int _id;
String courseTerm;
String courseId;
while (c.moveToNext()) {
_id = c.getInt(COLUMN_LVA_ID);
courseTerm = c.getString(COLUMN_LVA_TERM);
courseId = c.getString(COLUMN_LVA_COURSEID);
// update only lvas from loaded terms, ignore all other
Term term = termMap.get(courseTerm);
if (term != null && term.isLoaded()) {
Course course = lvaMap.remove(KusssHelper.getCourseKey(term, courseId));
if (course != null) {
// Check to see if the entry needs to be
// updated
Uri existingUri = lvaUri
.buildUpon()
.appendPath(Integer.toString(_id))
.build();
Log.d(TAG, "Scheduling update: "
+ existingUri);
batch.add(ContentProviderOperation
.newUpdate(
KusssContentContract
.asEventSyncAdapter(
existingUri,
mAccount.name,
mAccount.type))
.withValue(
KusssContentContract.Course.COL_ID,
Integer.toString(_id))
.withValues(KusssHelper.getLvaContentValues(course))
.build());
mSyncResult.stats.numUpdates++;
} else {
// delete
Log.d(TAG,
"delete: "
+ KusssHelper.getCourseKey(term, courseId));
// Entry doesn't exist. Remove only
// newer
// events from the database.
Uri deleteUri = lvaUri
.buildUpon()
.appendPath(Integer.toString(_id))
.build();
Log.d(TAG, "Scheduling delete: "
+ deleteUri);
batch.add(ContentProviderOperation
.newDelete(
KusssContentContract
.asEventSyncAdapter(
deleteUri,
mAccount.name,
mAccount.type))
.build());
mSyncResult.stats.numDeletes++;
}
} else {
mSyncResult.stats.numSkippedEntries++;
}
}
c.close();
for (Course course : lvaMap.values()) {
// insert only lvas from loaded terms, ignore all other
Term term = termMap.get(course.getTerm().toString());
if (term != null && term.isLoaded()) {
batch.add(ContentProviderOperation
.newInsert(
KusssContentContract
.asEventSyncAdapter(
lvaUri,
mAccount.name,
mAccount.type))
.withValues(KusssHelper.getLvaContentValues(course))
.build());
Log.d(TAG,
"Scheduling insert: " + course.getTerm()
+ " " + course.getCourseId());
mSyncResult.stats.numInserts++;
} else {
mSyncResult.stats.numSkippedEntries++;
}
}
if (batch.size() > 0) {
updateNotify(mContext.getString(R.string.notification_sync_lva_saving));
Log.d(TAG, "Applying batch update");
mProvider.applyBatch(batch);
Log.d(TAG, "Notify resolver");
mResolver
.notifyChange(
KusssContentContract.Course.CONTENT_CHANGED_URI,
null, // No
// local
// observer
false); // IMPORTANT: Do not
// sync to
// network
} else {
Log.w(TAG,
"No batch operations found! Do nothing");
}
}
}
KusssHandler.getInstance().logout(mContext);
} else {
mSyncResult.stats.numAuthExceptions++;
}
} catch (Exception e) {
Analytics.sendException(mContext, e, true);
Log.e(TAG, "import failed", e);
}
if (mUpdateNotification != null) {
mUpdateNotification.cancel();
}
if (mReleaseProvider) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mProvider.close();
} else {
mProvider.release();
}
}
return null;
}
}