Java tutorial
/******************************************************************************* * Copyright (c) 2013 Humberto Fraga <xisberto@gmail.com>. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * Humberto Fraga <xisberto@gmail.com> - initial API and implementation ******************************************************************************/ package net.xisberto.phonetodesktop.network; import android.app.IntentService; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat.Builder; import android.support.v4.app.TaskStackBuilder; import android.support.v4.content.LocalBroadcastManager; 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.extensions.android.gms.auth.UserRecoverableAuthIOException; 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.services.tasks.Tasks; import com.google.api.services.tasks.model.Task; import net.xisberto.phonetodesktop.Preferences; import net.xisberto.phonetodesktop.R; import net.xisberto.phonetodesktop.Utils; import net.xisberto.phonetodesktop.database.DatabaseHelper; import net.xisberto.phonetodesktop.model.LocalTask; import net.xisberto.phonetodesktop.model.LocalTask.Options; import net.xisberto.phonetodesktop.model.LocalTask.PersistCallback; import net.xisberto.phonetodesktop.model.LocalTask.Status; import net.xisberto.phonetodesktop.ui.MainActivity; import net.xisberto.phonetodesktop.ui.WaitListActivity; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * An {@link IntentService} subclass for handling asynchronous task requests in * a service on a separate handler thread. * <p> */ public class GoogleTasksService extends IntentService { private static final int NOTIFICATION_SEND = 42, NOTIFICATION_SEND_LATER = 3, NOTIFICATION_ERROR = 1, NOTIFICATION_NEED_AUTHORIZE = 2; protected com.google.api.services.tasks.Tasks client; private GoogleAccountCredential credential; private HttpTransport transport; private JsonFactory jsonFactory; private Preferences preferences; private String list_id; private String[] cache_unshorten, cache_titles; public GoogleTasksService() { super("GoogleTasksService"); } @Override public void onCreate() { super.onCreate(); preferences = new Preferences(this); list_id = preferences.loadListId(); credential = GoogleAccountCredential.usingOAuth2(this, Utils.scopes); credential.setSelectedAccountName(preferences.loadAccountName()); transport = AndroidHttp.newCompatibleTransport(); jsonFactory = new GsonFactory(); client = new com.google.api.services.tasks.Tasks.Builder(transport, jsonFactory, credential) .setApplicationName("PhoneToDesktop").build(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Utils.log("onStartCommand " + intent.getAction()); if (Utils.ACTION_SEND_TASKS.equals(intent.getAction())) { // We start foregroud as soon as we receive an ACTION_SEND_TASKS // action. This will make the service foreground when // SendTaskActivity goes away startForeground(NOTIFICATION_SEND, buildNotification(NOTIFICATION_SEND).build()); } return super.onStartCommand(intent, flags, startId); } @Override protected void onHandleIntent(Intent intent) { if (intent != null) { final String action = intent.getAction(); Utils.log("onHandleIntent " + intent.getAction()); long[] tasks_ids; try { if (action.equals(Utils.ACTION_PROCESS_TASK)) { long task_id = intent.getLongExtra(Utils.EXTRA_TASK_ID, -1); LocalTask task = DatabaseHelper.getInstance(this).getTask(task_id); final Intent result = new Intent(Utils.ACTION_RESULT_PROCESS_TASK); result.putExtra(Utils.EXTRA_TASK_ID, task.getLocalId()); if (isOnline()) { processOptions(task); task.persistBlocking(new PersistCallback() { @Override public void run() { if (cache_unshorten != null) { result.putExtra(Utils.EXTRA_CACHE_UNSHORTEN, cache_unshorten); } if (cache_titles != null) { result.putExtra(Utils.EXTRA_CACHE_TITLES, cache_titles); } LocalBroadcastManager.getInstance(GoogleTasksService.this).sendBroadcast(result); } }); } else { tasks_ids = intent.getLongArrayExtra(Utils.EXTRA_TASKS_IDS); revertTaskToReady(tasks_ids); LocalBroadcastManager.getInstance(this).sendBroadcast(result); } } else if (action.equals(Utils.ACTION_SEND_TASKS)) { if (isOnline()) { tasks_ids = intent.getLongArrayExtra(Utils.EXTRA_TASKS_IDS); if (tasks_ids.length == 1) { DatabaseHelper databaseHelper = DatabaseHelper.getInstance(this); LocalTask task = databaseHelper.getTask(tasks_ids[0]); handleActionSend(task); } else { handleActionSendMultiple(tasks_ids); } stopForeground(true); } else { tasks_ids = intent.getLongArrayExtra(Utils.EXTRA_TASKS_IDS); revertTaskToReady(tasks_ids); ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify( NOTIFICATION_SEND_LATER, buildNotification(NOTIFICATION_SEND_LATER).build()); } } else if (action.equals(Utils.ACTION_LIST_TASKS)) { handleActionList(); } else if (action.equals(Utils.ACTION_REMOVE_TASKS)) { handleActionRemove(intent.getStringArrayListExtra(Utils.EXTRA_TASKS_IDS)); } } catch (UserRecoverableAuthIOException userRecoverableException) { Utils.log(Log.getStackTraceString(userRecoverableException)); stopForeground(true); tasks_ids = intent.getLongArrayExtra(Utils.EXTRA_TASKS_IDS); revertTaskToReady(tasks_ids); ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(NOTIFICATION_NEED_AUTHORIZE, buildNotification(NOTIFICATION_NEED_AUTHORIZE).build()); } catch (IOException ioException) { Utils.log(Log.getStackTraceString(ioException)); if (action.equals(Utils.ACTION_SEND_TASKS)) { tasks_ids = intent.getLongArrayExtra(Utils.EXTRA_TASKS_IDS); stopForeground(true); revertTaskToReady(tasks_ids); ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(NOTIFICATION_ERROR, buildNotification(NOTIFICATION_ERROR).build()); } else { Intent broadcast = new Intent(Utils.ACTION_LIST_TASKS); broadcast.putExtra(Utils.EXTRA_ERROR_TEXT, getString(R.string.txt_error_list)); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } } catch (NullPointerException npe) { tasks_ids = intent.getLongArrayExtra(Utils.EXTRA_TASKS_IDS); Utils.log(Log.getStackTraceString(npe)); stopForeground(true); revertTaskToReady(tasks_ids); ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(NOTIFICATION_NEED_AUTHORIZE, buildNotification(NOTIFICATION_NEED_AUTHORIZE).build()); } } } @Override public void onDestroy() { super.onDestroy(); Utils.log("Destroying service"); } private NotificationCompat.Builder buildNotification(int notif_id) { Builder builder = new NotificationCompat.Builder(this).setWhen(System.currentTimeMillis()); // The default notification send the user to the waiting list Intent intentContent = new Intent(this, WaitListActivity.class); TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); stackBuilder.addParentStack(WaitListActivity.class); stackBuilder.addNextIntent(intentContent); PendingIntent pendingContent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(pendingContent); switch (notif_id) { case NOTIFICATION_SEND: builder.setSmallIcon(android.R.drawable.stat_sys_upload).setTicker(getString(R.string.txt_sending)) .setContentTitle(getString(R.string.txt_sending)); return builder; case NOTIFICATION_SEND_LATER: builder.setAutoCancel(true).setSmallIcon(android.R.drawable.stat_notify_error) .setTicker(getString(R.string.txt_error_no_connection)) .setContentTitle(getString(R.string.txt_error_no_connection)) .setContentText(getString(R.string.txt_error_try_again)); return builder; case NOTIFICATION_ERROR: builder.setAutoCancel(true).setSmallIcon(android.R.drawable.stat_notify_error) .setTicker(getString(R.string.txt_error_sending)) .setContentTitle(getString(R.string.txt_error_sending)) .setContentText(getString(R.string.txt_error_try_again)); return builder; case NOTIFICATION_NEED_AUTHORIZE: // When authorization is need, send the user to authorization // process intentContent.setClass(this, MainActivity.class); intentContent.setAction(Utils.ACTION_AUTHENTICATE); intentContent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingAuthorize = PendingIntent.getActivity(this, 0, intentContent, PendingIntent.FLAG_CANCEL_CURRENT); builder.setContentIntent(pendingAuthorize).setAutoCancel(true) .setSmallIcon(android.R.drawable.stat_notify_error) .setTicker(getString(R.string.txt_error_sending)) .setContentTitle(getString(R.string.txt_error_sending)) .setContentText(getString(R.string.txt_need_authorize)); return builder; default: return null; } } private void handleActionSend(LocalTask task) throws UserRecoverableAuthIOException, IOException { PersistCallback callback = new PersistCallback() { @Override public void run() { LocalBroadcastManager.getInstance(GoogleTasksService.this) .sendBroadcast(new Intent(Utils.ACTION_LIST_LOCAL_TASKS)); } }; task.setStatus(Status.SENDING).persist(callback); Utils.log("Sending task " + task.getTitle()); Task new_task = new Task().setTitle(task.getTitle()); client.tasks().insert(list_id, new_task).execute(); task.setStatus(Status.SENT).persist(callback); } private void handleActionSendMultiple(long... tasks_ids) throws UserRecoverableAuthIOException, IOException { Builder builder = buildNotification(NOTIFICATION_SEND); int i = 0; for (long task_id : tasks_ids) { String contentText = getString(R.string.txt_sending_multiple); contentText = String.format(contentText, i + 1, tasks_ids.length); builder.setContentText(contentText); ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(NOTIFICATION_SEND, builder.build()); DatabaseHelper databaseHelper = DatabaseHelper.getInstance(this); LocalTask task = databaseHelper.getTask(task_id); handleActionSend(task); } } private void handleActionList() throws UserRecoverableAuthIOException, IOException { List<Task> list = client.tasks().list(list_id).execute().getItems(); if (list != null) { ArrayList<String> ids = new ArrayList<String>(); ArrayList<String> titles = new ArrayList<String>(); for (Task task : list) { ids.add(task.getId()); titles.add(task.getTitle()); } Intent broadcast = new Intent(Utils.ACTION_LIST_TASKS); broadcast.putStringArrayListExtra(Utils.EXTRA_IDS, ids); broadcast.putStringArrayListExtra(Utils.EXTRA_TITLES, titles); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } } private void handleActionRemove(ArrayList<String> tasks_ids) throws UserRecoverableAuthIOException, IOException { Tasks.TasksOperations tasks = client.tasks(); for (String task_id : tasks_ids) { tasks.delete(list_id, task_id).execute(); } handleActionList(); } private void revertTaskToReady(long... local_ids) { if (local_ids != null) { DatabaseHelper databaseHelper = DatabaseHelper.getInstance(this); databaseHelper.setStatus(Status.READY, local_ids); } } private void processOptions(LocalTask task) throws IOException { URLOptions urlOptions = new URLOptions(); String[] parts; cache_unshorten = null; cache_titles = null; switch (task.getStatus()) { case ADDED: case READY: if (task.hasOption(Options.OPTION_UNSHORTEN)) { task.setStatus(Status.PROCESSING_UNSHORTEN).persist(); String links = Utils.filterLinks(task.getTitle()).trim(); parts = urlOptions.unshorten(links.split(" ")); cache_unshorten = parts.clone(); task.setTitle(Utils.replace(task.getTitle(), parts)).removeOption(Options.OPTION_UNSHORTEN); } if (task.hasOption(Options.OPTION_GETTITLES)) { task.setStatus(Status.PROCESSING_TITLE).persist(); String links = Utils.filterLinks(task.getTitle()).trim(); parts = urlOptions.getTitles(links.split(" ")); cache_titles = parts.clone(); task.setTitle(Utils.appendInBrackets(task.getTitle(), parts)) .removeOption(Options.OPTION_GETTITLES); } task.setStatus(Status.READY); break; case PROCESSING_UNSHORTEN: parts = Utils.filterLinks(task.getTitle()).split(" "); parts = urlOptions.unshorten(parts); cache_unshorten = parts.clone(); task.setTitle(Utils.replace(task.getTitle(), parts)).removeOption(Options.OPTION_UNSHORTEN); if (!task.hasOption(Options.OPTION_GETTITLES)) { task.setStatus(Status.READY); break; } case PROCESSING_TITLE: parts = Utils.filterLinks(task.getTitle()).split(" "); parts = urlOptions.getTitles(parts); cache_titles = parts.clone(); task.setTitle(Utils.appendInBrackets(task.getTitle(), parts)).removeOption(Options.OPTION_GETTITLES) .setStatus(Status.READY); break; } } private boolean isOnline() { ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); if (netInfo != null && netInfo.isConnectedOrConnecting()) { return true; } return false; } }