package de.tum.in.tumcampusapp.managers;
import android.Manifest;
import android.app.IntentService;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.provider.CalendarContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import com.google.common.base.Optional;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import de.tum.in.tumcampusapp.R;
import de.tum.in.tumcampusapp.auxiliary.Const;
import de.tum.in.tumcampusapp.auxiliary.Utils;
import de.tum.in.tumcampusapp.auxiliary.calendar.CalendarHelper;
import de.tum.in.tumcampusapp.cards.NextLectureCard;
import de.tum.in.tumcampusapp.cards.generic.Card;
import de.tum.in.tumcampusapp.models.tumo.CalendarRow;
import de.tum.in.tumcampusapp.models.tumo.CalendarRowSet;
import de.tum.in.tumcampusapp.models.tumo.Geo;
/**
* Calendar Manager, handles database stuff, external imports
*/
public class CalendarManager extends AbstractManager implements Card.ProvidesCard {
private static final String[] PROJECTION = {"_id", "name"};
private static final int TIME_TO_SYNC_CALENDAR = 604800; // 1 week
public CalendarManager(Context context) {
super(context);
// create table if needed
db.execSQL("CREATE TABLE IF NOT EXISTS room_locations ("
+ "title VARCHAR PRIMARY KEY, latitude VARCHAR, longitude VARCHAR)");
db.execSQL("CREATE TABLE IF NOT EXISTS calendar ("
+ "nr VARCHAR PRIMARY KEY, status VARCHAR, url VARCHAR, "
+ "title VARCHAR, description VARCHAR, dtstart VARCHAR, dtend VARCHAR, "
+ "location VARCHAR REFERENCES room_locations)");
}
/**
* Replaces the current TUM_CAMPUS_APP calendar with a new version
*
* @param c Context
*/
public static void syncCalendar(Context c) {
// Deleting earlier calendar created by TUM Campus App
deleteLocalCalendar(c);
Uri uri = CalendarHelper.addCalendar(c);
addEvents(c, uri);
}
/**
* Deletes a local Google calendar
*
* @return Number of rows deleted
*/
public static int deleteLocalCalendar(Context c) {
return CalendarHelper.deleteCalendar(c);
}
private static void addEvents(Context c, Uri uri) {
if (ActivityCompat.checkSelfPermission(c, Manifest.permission.WRITE_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
return;
}
// Get ID
ContentResolver contentResolver = c.getContentResolver();
Cursor cursor = contentResolver.query(uri, PROJECTION, null, null, null);
String id = "0";
while (cursor.moveToNext()) {
id = cursor.getString(0);
}
cursor.close();
CalendarManager calendarManager = new CalendarManager(c);
Date dtstart;
Date dtend;
// Get all calendar items from database
cursor = calendarManager.getAllFromDb();
while (cursor.moveToNext()) {
// Get each table row
//final String status = cursor.getString(1);
final String title = cursor.getString(3);
final String description = cursor.getString(4);
final String strStart = cursor.getString(5);
final String strEnd = cursor.getString(6);
final String location = cursor.getString(7);
try {
// Get the correct date and time from database
dtstart = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ENGLISH).parse(strStart);
dtend = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ENGLISH).parse(strEnd);
Calendar beginTime = Calendar.getInstance();
beginTime.setTime(dtstart);
Calendar endTime = Calendar.getInstance();
endTime.setTime(dtend);
// Get start and end time
long startMillis = beginTime.getTimeInMillis();
long endMillis = endTime.getTimeInMillis();
ContentValues values = new ContentValues();
// Put the received values into a contentResolver to
// transmit the to Google Calendar
values.put(CalendarContract.Events.DTSTART, startMillis);
values.put(CalendarContract.Events.DTEND, endMillis);
values.put(CalendarContract.Events.TITLE, title);
values.put(CalendarContract.Events.DESCRIPTION, description);
values.put(CalendarContract.Events.CALENDAR_ID, id);
values.put(CalendarContract.Events.EVENT_LOCATION, location);
values.put(CalendarContract.Events.EVENT_TIMEZONE, R.string.calendarTimeZone);
contentResolver.insert(CalendarContract.Events.CONTENT_URI, values);
} catch (ParseException e) {
Utils.log(e);
}
}
}
/**
* Returns all stored events from db
*
* @return Cursor with all calendar events. Columns are
* (nr, status, url, title, description, dtstart, dtend, location)
*/
Cursor getAllFromDb() {
return db.rawQuery("SELECT * FROM calendar WHERE status!=\"CANCEL\"", null);
}
public Cursor getFromDbForDate(Date date) {
// Format the requested date
String requestedDateString = Utils.getDateString(date);
// Fetch the data
return db.rawQuery("SELECT * FROM calendar WHERE dtstart LIKE ? AND status!=\"CANCEL\" ORDER BY dtstart ASC", new String[]{"%" + requestedDateString + "%"});
}
/**
* Get current lecture from the database
*
* @return Database cursor (name, location, _id)
*/
public Cursor getCurrentFromDb() {
return db.rawQuery("SELECT title, location, nr, dtend FROM calendar WHERE datetime('now', 'localtime') BETWEEN dtstart AND dtend AND status!=\"CANCEL\"", null);
}
/**
* Checks if there are any event in the database
*
* @return True if there are lectures in the database, false if there is no lecture
*/
public boolean hasLectures() {
boolean result = false;
Cursor c = db.rawQuery("SELECT nr FROM calendar", null);
if (c.moveToNext()) {
result = true;
}
c.close();
return result;
}
public void importCalendar(CalendarRowSet myCalendarList) {
// Cleanup cache before importing
removeCache();
// reading xml
List<CalendarRow> myCalendar = myCalendarList.getKalendarList();
if (myCalendar != null) {
for (CalendarRow row : myCalendar) {
// insert into database
try {
replaceIntoDb(row);
} catch (Exception e) {
Utils.log(e);
}
}
}
new SyncManager(mContext).replaceIntoDb(Const.SYNC_CALENDAR_IMPORT);
}
/**
* Removes all cache items
*/
public void removeCache() {
db.execSQL("DELETE FROM calendar");
}
void replaceIntoDb(CalendarRow row) {
if (row.getNr().isEmpty()) {
throw new IllegalArgumentException("Invalid id.");
}
if (row.getTitle().isEmpty()) {
throw new IllegalArgumentException("Invalid lecture Title.");
}
db.execSQL("REPLACE INTO calendar (nr, status, url, title, "
+ "description, dtstart, dtend, location) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
new String[]{row.getNr(), row.getStatus(), row.getUrl(),
row.getTitle(), row.getDescription(),
row.getDtstart(), row.getDtend(), row.getLocation()});
}
/**
* Gets the next lectures that could be important to the user
*/
public Cursor getNextCalendarItem() {
return db.rawQuery("SELECT title, dtstart, dtend, location FROM calendar JOIN " +
"(SELECT dtstart AS maxstart FROM calendar WHERE status!=\"CANCEL\" AND datetime('now', 'localtime')<dtstart " +
"ORDER BY dtstart LIMIT 1) ON status!=\"CANCEL\" AND datetime('now', 'localtime')<dtend AND dtstart<=maxstart " +
"ORDER BY dtend, dtstart LIMIT 4", null);
}
/**
* Gets the coordinates of the next lecture or the current running lecture,
* if it started during the last 30 minutes
*/
public Geo getNextCalendarItemGeo() {
Cursor cur = db.rawQuery("SELECT r.latitude, r.longitude " +
"FROM calendar c, room_locations r " +
"WHERE datetime('now', 'localtime') < datetime(c.dtstart, '+1800 seconds') AND " +
"datetime('now','localtime') < c.dtend AND r.title == c.location AND c.status!=\"CANCEL\"" +
"ORDER BY dtstart LIMIT 1", null);
Geo geo = null;
if (cur.moveToFirst()) {
geo = new Geo(cur.getDouble(0), cur.getDouble(1));
}
cur.close();
return geo;
}
/**
* Shows next lecture card if lecture is available
*
* @param context Context
*/
@Override
public void onRequestCard(Context context) {
Cursor rows = getNextCalendarItem();
if (rows.moveToFirst()) {
NextLectureCard card = new NextLectureCard(context);
card.setLectures(rows);
card.apply();
}
}
public static class QueryLocationsService extends IntentService {
private static final String QUERY_LOCATIONS = "query_locations";
public QueryLocationsService() {
super(QUERY_LOCATIONS);
}
public static void loadGeo(Context c) {
LocationManager locationManager = new LocationManager(c);
SQLiteDatabase db = getDb(c);
Cursor cur = db.rawQuery("SELECT c.location " +
"FROM calendar c LEFT JOIN room_locations r ON " +
"c.location=r.title " +
"WHERE r.latitude IS NULL " +
"GROUP BY c.location", null);
// Retrieve geo from room name
if (cur.moveToFirst()) {
do {
String location = cur.getString(0);
if (location == null || location.isEmpty()) {
continue;
}
Optional<Geo> geo = locationManager.roomLocationStringToGeo(location);
if (geo.isPresent()) {
Utils.logv("inserted " + location + ' ' + geo);
db.execSQL("REPLACE INTO room_locations (title, latitude, longitude) VALUES (?, ?, ?)",
new String[]{location, geo.get().getLatitude(), geo.get().getLongitude()});
}
} while (cur.moveToNext());
}
cur.close();
// Do sync of google calendar if necessary
boolean syncCalendar = Utils.getInternalSettingBool(c, Const.SYNC_CALENDAR, false)
&& ContextCompat.checkSelfPermission(c, Manifest.permission.WRITE_CALENDAR) == PackageManager.PERMISSION_GRANTED;
if (syncCalendar && new SyncManager(c).needSync(Const.SYNC_CALENDAR, TIME_TO_SYNC_CALENDAR)) {
syncCalendar(c);
new SyncManager(c).replaceIntoDb(Const.SYNC_CALENDAR);
}
}
@Override
protected void onHandleIntent(Intent intent) {
new Thread(new Runnable() {
@Override
public void run() {
loadGeo(QueryLocationsService.this);
}
}).start();
}
}
}