package se.slide.timy.service; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.IBinder; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.support.v4.app.TaskStackBuilder; import android.text.format.DateFormat; import android.util.Log; //import com.google.api.client.extensions.android.http.AndroidHttp; import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential; import com.google.api.client.googleapis.json.GoogleJsonResponseException; import com.google.api.client.http.HttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.gson.GsonFactory; import com.google.api.client.util.DateTime; import com.google.api.services.calendar.CalendarScopes; import com.google.api.services.calendar.model.Event; import com.google.api.services.calendar.model.EventDateTime; import se.slide.timy.MainActivity; import se.slide.timy.R; import se.slide.timy.ShowNotification; import se.slide.timy.db.DatabaseManager; import se.slide.timy.model.Color; import se.slide.timy.model.Project; import se.slide.timy.model.Report; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.List; public class SyncService extends Service { public static final int GOOD_RESULT = 0; public static final int ERROR_BAD_ACCOUNT = 1; public static final int ERROR_BAD_CALENDAR = 2; public static final int ERROR_NETWORK = 3; public final static int NOTIFICATION_ID = 1; private static final String TAG = "SyncService"; private boolean mIsTaskRunning; public int retries = 0; public static final int MAX_RETRIES = 10; private CreateCalendarEventsTask mTask; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { Log.d(TAG, "Created service"); // ORMLite needs to be initiated DatabaseManager.init(this); mIsTaskRunning = false; } /* * (non-Javadoc) * @see android.app.Service#onStartCommand(android.content.Intent, int, int) */ @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "Started service"); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); boolean syncGoogle = sharedPreferences.getBoolean("sync_google", false); if (syncGoogle) createEvents(false); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { Log.d(TAG, "Destroyed service."); mIsTaskRunning = false; } // Uses AsyncTask to download the XML feed from the server. public void createEvents(boolean retry) { if (mTask == null || retry) mTask = createTask(); if (!mTask.getStatus().equals(AsyncTask.Status.PENDING)) mTask = createTask(); if (!mIsTaskRunning) mTask.execute(); } public void runAgain(List<Project> projects) { updateSyncedEvents(projects); // Check to see if we got reports added to the database while we were // busy creating calendar events if (DatabaseManager.getInstance().haveUnsyncedReports()) createEvents(false); } public void updateSyncedEvents(List<Project> projects) { for (int i = 0; i < projects.size(); i++) { List<Report> reports = projects.get(i).getReports(); for (int a = 0; a < reports.size(); a++) DatabaseManager.getInstance().updateReport(reports.get(a)); } } private CreateCalendarEventsTask createTask() { Log.d(TAG, "Create new task"); String accountName = PreferenceManager.getDefaultSharedPreferences(this).getString( "sync_google_calendar_account", null); String calendarId = PreferenceManager.getDefaultSharedPreferences(this).getString( "sync_google_calendar_calendar_id", null); List<Project> projects = DatabaseManager.getInstance().getProjectsWithUnsyncedReports(); List<Color> colors = DatabaseManager.getInstance().getColors(); return new CreateCalendarEventsTask(this, accountName, calendarId, projects, colors); } private class CreateCalendarEventsTask extends AsyncTask<String, Void, Integer> { private WeakReference<Service> weakService; private String accountName; private String calendarId; private List<Project> projects; private List<Color> colors; public CreateCalendarEventsTask(Service service, String accountName, String calendarId, List<Project> projects, List<Color> colors) { weakService = new WeakReference<Service>(service); this.accountName = accountName; this.calendarId = calendarId; this.projects = projects; this.colors = colors; } /* * (non-Javadoc) * @see android.os.AsyncTask#onPreExecute() */ @Override protected void onPreExecute() { super.onPreExecute(); mIsTaskRunning = true; } @Override protected Integer doInBackground(String... urls) { int result = SyncService.GOOD_RESULT; /* if (accountName == null) return SyncService.ERROR_BAD_ACCOUNT; if (calendarId == null) return SyncService.ERROR_BAD_CALENDAR; GoogleAccountCredential credential; final com.google.api.services.calendar.Calendar client; final HttpTransport transport = AndroidHttp.newCompatibleTransport(); final JsonFactory jsonFactory = new GsonFactory(); // Google Accounts credential = GoogleAccountCredential.usingOAuth2(weakService.get(), CalendarScopes.CALENDAR); credential.setSelectedAccountName(accountName); // Calendar client client = new com.google.api.services.calendar.Calendar.Builder( transport, jsonFactory, credential).setApplicationName("Timy/1.0") .build(); for (int i = 0; i < projects.size(); i++) { Project project = projects.get(i); List<Report> reports = project.getReports(); for (int a = 0; a < reports.size(); a++) { Report report = reports.get(a); String allDayTime = DateFormat.format("yyyy-MM-dd", report.getDate()) .toString(); DateTime dt = new DateTime(allDayTime); StringBuilder builder = new StringBuilder(); builder.append(project.getName()); builder.append(": "); if (report.getHours() > 0) { builder.append(report.getHours()); builder.append("h"); } if (report.getMinutes() > 0) { builder.append(report.getMinutes()); builder.append("m"); } Event event = new Event(); event.setSummary(builder.toString()); event.setDescription(report.getComment()); event.setStart(new EventDateTime().setDate(dt)); event.setEnd(new EventDateTime().setDate(dt)); // We need to deal with bad colorIds int colorId = -1; try { colorId = Integer.valueOf(project.getColorId()); } catch (NumberFormatException e) { Log.w(TAG, "Bad color id, using default color"); } if (colorId > 0 && colorId <= colors.size()) event.setColorId(project.getColorId()); boolean update = false; if (report.getGoogleCalendarEventId() != null && report.getGoogleCalendarEventId().length() > 0) { update = true; event.setId(report.getGoogleCalendarEventId()); } try { Event createdEvent = null; if (update) createdEvent = client.events() .update(calendarId, report.getGoogleCalendarEventId(), event) .execute(); else createdEvent = client.events().insert(calendarId, event).execute(); report.setGoogleCalendarEventId(createdEvent.getId()); report.setGoogleCalendarSync(true); Log.d(TAG, "Created event"); } catch (GoogleJsonResponseException e) { if (e.getStatusCode() == 400) { Log.d(TAG, "Error creating event"); // The event has most likely been manually removed, // so clear the eventId report.setGoogleCalendarEventId(null); } e.printStackTrace(); result = SyncService.ERROR_NETWORK; } catch (IOException e) { e.printStackTrace(); result = SyncService.ERROR_NETWORK; } } } */ return result; } @Override protected void onPostExecute(Integer result) { super.onPostExecute(result); mIsTaskRunning = false; if (result == GOOD_RESULT) runAgain(projects); else if (result == ERROR_NETWORK) { if (retries++ < MAX_RETRIES) { Log.d(TAG, "Retry to create events"); updateSyncedEvents(projects); // Comment this line to test // MAX_RETRIES // TODO: Put in a sleep/wait before next retry createEvents(true); } else { retries = 0; // Show notification error ... use string resources ShowNotification.showNotification(getApplicationContext(), NOTIFICATION_ID, getString(R.string.error_sync_title), getString(R.string.error_bad_sync), ERROR_NETWORK); } } else if (result == ERROR_BAD_ACCOUNT) { // Show notification ShowNotification.showNotification(getApplicationContext(), NOTIFICATION_ID, getString(R.string.error_sync_title), getString(R.string.error_bad_account), ERROR_NETWORK); } else if (result == ERROR_BAD_CALENDAR) { // Show notification ShowNotification.showNotification(getApplicationContext(), NOTIFICATION_ID, getString(R.string.error_sync_title), getString(R.string.error_bad_calendar), ERROR_NETWORK); } } } private void showNotification(String title, String text) { NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.ic_launcher) .setAutoCancel(true) .setContentTitle(title) .setContentText(text); // Creates an explicit intent for an Activity in your app Intent resultIntent = new Intent(this, MainActivity.class); // The stack builder object will contain an artificial back stack for // the // started Activity. // This ensures that navigating backward from the Activity leads out of // your application to the Home screen. TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); // Adds the back stack for the Intent (but not the Intent itself) stackBuilder.addParentStack(MainActivity.class); // Adds the Intent that starts the Activity to the top of the stack stackBuilder.addNextIntent(resultIntent); PendingIntent resultPendingIntent = stackBuilder.getPendingIntent( 0, PendingIntent.FLAG_UPDATE_CURRENT ); mBuilder.setContentIntent(resultPendingIntent); NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); } }