All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.telly.groundy.Groundy Maven / Gradle / Ivy

/**
 * Copyright Telly, Inc. and other Groundy contributors.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the
 * following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package com.telly.groundy;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;

import android.os.ResultReceiver;
import java.io.Serializable;
import java.util.ArrayList;

public final class Groundy implements Parcelable {
  /**
   * Key used by the {@link com.telly.groundy.annotations.OnProgress} callback to specify
   * the progress of the task. Parameters annotated with this key must be int.
   */
  public static final String PROGRESS = "com.telly.groundy.key.PROGRESS";

  /**
   * If the task crashed, the {@link com.telly.groundy.annotations.OnFailure} callback will
   * be invoked and it's possible to receive the exception message by annotating a parameter with
   * this key, e.g.
   *
   * 
{@code
   *
   * @OnFailure public void onFail(@Param(Groundy.CRASH_MESSAGE) crashMessage) {
   * // do something nice
   * }
   * }
*/ public static final String CRASH_MESSAGE = "com.telly.groundy.key.ERROR"; /** * Every callback can receive the task id by annotating a parameter with this key. The task * id is a long timestamp generated when the Groundy task is created. */ public static final String TASK_ID = "com.telly.groundy.key.TASK_ID"; /** * If the task was cancelled, the {@link com.telly.groundy.annotations.OnCancel} callback will * be invoked and it's possible to receive the cancel reason by annotating a parameter with * this key. It must be an integer. */ public static final String CANCEL_REASON = "com.telly.groundy.key.CANCEL_REASON"; /** * Every callback can receive the original arguments sent to the task by annotating a parameter * with this key. */ public static final String ORIGINAL_PARAMS = "com.telly.groundy.key.ORIGINAL_ARGS"; /** The class used to implement the executed task. */ public static final String TASK_IMPLEMENTATION = "com.telly.groundy.key.TASK_IMPLEMENTATION"; /** Progress value returned when it's not possible to determine the size of a file. **/ public static final int NO_SIZE_AVAILABLE = Integer.MIN_VALUE; /** * If true, the stack trace of the call for each groundy task will be send to the service. * It allows to know which piece of code invoked this task.* */ public static boolean devMode = false; static final String KEY_ARGUMENTS = "com.telly.groundy.key.ARGS"; static final String STACK_TRACE = "com.telly.groundy.key.STACK_TRACE"; static final String KEY_RECEIVER = "com.telly.groundy.key.RECEIVER"; static final String KEY_TASK = "com.telly.groundy.key.TASK"; static final String KEY_GROUP_ID = "com.telly.groundy.key.GROUP_ID"; static final String KEY_CALLBACK_ANNOTATION = "com.telly.groundy.key.CALLBACK_ANNOTATION"; static final String KEY_CALLBACK_NAME = "com.telly.groundy.key.CALLBACK_NAME"; private final Class mGroundyTask; private final long mId; private CallbacksReceiver mReceiver; private final Bundle mArgs = new Bundle(); private int mGroupId; private boolean mAlreadyProcessed = false; private CallbacksManager mCallbacksManager; private Class mGroundyClass = GroundyService.class; private boolean mAllowNonUIThreadCallbacks = false; private Groundy(Class groundyTask) { mGroundyTask = groundyTask; mId = System.nanoTime(); } private Groundy(Class groundyTask, long id) { mGroundyTask = groundyTask; mId = id; } /** * Creates a new Groundy instance ready to be queued or executed. You can configure it by adding * arguments ({@link #args(android.os.Bundle)}), setting a group id ({@link #group(int)}) or * providing a callback ({@link #callback(Object...)}). *

* You must configure the value before queueing or executing it. * * @param groundyTask reference of the groundy value implementation * @return new Groundy instance (does not execute anything) */ public static Groundy create(Class groundyTask) { if (groundyTask == null) { throw new IllegalStateException("GroundyTask no provided"); } return new Groundy(groundyTask); } /** * Set the arguments needed to run the task. * * @param arguments a bundle of arguments * @return itself */ public Groundy args(Bundle arguments) { checkAlreadyProcessed(); mArgs.putAll(arguments); return this; } /** * Allows this value to receive callback messages on non UI threads. * * @return itself */ public Groundy allowNonUiCallbacks() { checkAlreadyProcessed(); mAllowNonUIThreadCallbacks = true; return this; } /** * @param callbacks callbacks to register for this value * @return itself */ public Groundy callback(Object... callbacks) { if (callbacks == null || callbacks.length == 0) { throw new IllegalArgumentException("You must pass at least one callback handler"); } if (mReceiver != null) { throw new IllegalStateException("callback method can only be called once"); } checkAlreadyProcessed(); if (!mAllowNonUIThreadCallbacks && Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException( "callbacks can only be set on the UI thread. If you are sure you can handle callbacks " + "from a non UI thread, call Groundy#allowNonUiCallbacks() method first"); } mReceiver = new CallbacksReceiver(mGroundyTask, callbacks); return this; } /** * This allows you to set an identification groupId to the value which can be later used to * cancel it. Group ids can be shared by several groundy tasks even if their implementation is * different. * If cancelling tasks using a groupId, all tasks created with this groupId will be cancelled * and/or removed from the queue. * * @param groupId groupId for this value * @return itself */ public Groundy group(int groupId) { if (groupId <= 0) { throw new IllegalStateException("Group id must be greater than zero"); } checkAlreadyProcessed(); mGroupId = groupId; return this; } /** * This allows you to use a different GroundyService implementation. * * @param groundyClass a different Groundy service implementation * @return itself */ public Groundy service(Class groundyClass) { if (groundyClass == GroundyService.class) { throw new IllegalStateException( "This method is meant to set a different GroundyService implementation. " + "You cannot use GroundyService.class, http://i.imgur.com/IR23PAe.png"); } checkAlreadyProcessed(); mGroundyClass = groundyClass; return this; } /** * Sets a callback manager for this value. It allows you to easily attach/detach your callbacks * on * configuration change. This is important if you are not handling the configuration changes by * your self, since it will prevent leaks or wrong results when callbacks are invoked. * * @param callbacksManager a callback manager instance * @return itself */ public Groundy callbackManager(CallbacksManager callbacksManager) { checkAlreadyProcessed(); mCallbacksManager = callbacksManager; return this; } /** * Queues a value to the Groundy Service. This value won't be executed until the previous queued * tasks are done. If you need your value to execute right away use the {@link * Groundy#executeUsing(Context)} method. * * @param context used to start the Groundy service * @return a {@link TaskHandler} that can be used to clear the task callbacks list or cancel it */ public TaskHandler queueUsing(Context context) { boolean async = false; return internalQueueOrExecute(context, async); } /** * Execute a value right away. * * @param context used to start the Groundy service * @return a {@link TaskHandler} that can be used to clear the task callbacks list or cancel it */ public TaskHandler executeUsing(Context context) { boolean async = true; return internalQueueOrExecute(context, async); } /** * Creates an intent that can be used to execute this task by invoking context.startService(). * * @param context used to start the groundy service * @param async true if the task must be executed right away, false if it must be queued * @return a unique number assigned to this value */ public Intent asIntent(Context context, boolean async) { markAsProcessed(); return internalGetServiceIntent(context, async); } private TaskHandler internalQueueOrExecute(Context context, boolean async) { markAsProcessed(); TaskHandler taskProxy = new TaskHandlerImpl(this); if (mCallbacksManager != null) { mCallbacksManager.register(taskProxy); } context.startService(internalGetServiceIntent(context, async)); return taskProxy; } long getId() { return mId; } Class getGroundyServiceClass() { return mGroundyClass; } Class getGroundyTaskClass() { return mGroundyTask; } CallbacksReceiver getReceiver() { return mReceiver; } private void checkAlreadyProcessed() { if (mAlreadyProcessed) { throw new IllegalStateException("This method can only be called before queueUsing(), " + "executeUsing() or asIntent() methods"); } } private void markAsProcessed() { if (mAlreadyProcessed) { throw new IllegalStateException("Task already queued or executed"); } mAlreadyProcessed = true; } private Intent internalGetServiceIntent(Context context, boolean async) { Intent intent = new Intent(context, mGroundyClass); intent.setAction(async ? GroundyService.ACTION_EXECUTE : GroundyService.ACTION_QUEUE); intent.putExtra(KEY_ARGUMENTS, mArgs); if (devMode) { StackTraceElement[] stackTrace = new Throwable().getStackTrace(); intent.putExtra(STACK_TRACE, stackTrace); } if (mReceiver != null) { intent.putExtra(KEY_RECEIVER, mReceiver); } intent.putExtra(KEY_TASK, mGroundyTask); intent.putExtra(TASK_ID, mId); intent.putExtra(KEY_GROUP_ID, mGroupId); return intent; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Groundy)) { return false; } Groundy groundy = (Groundy) o; if (mId != groundy.mId) return false; if (!mGroundyTask.equals(groundy.mGroundyTask)) return false; return true; } @Override public int hashCode() { int result = mGroundyTask.hashCode(); result = 31 * result + (int) (mId ^ (mId >>> 32)); return result; } @Override public String toString() { return "Groundy{" + ", groundyTask=" + mGroundyTask + ", resultReceiver=" + mReceiver + ", extras=" + mArgs + ", groupId=" + mGroupId + '}'; } @SuppressWarnings("UnusedDeclaration") public static final Creator CREATOR = new Creator() { @Override public Groundy createFromParcel(Parcel source) { Class groundyTask = (Class) source.readSerializable(); long id = source.readLong(); boolean hadReceiver = source.readByte() == 1; //noinspection unchecked Groundy groundy = new Groundy(groundyTask, id); if (hadReceiver) { ResultReceiver r = source.readParcelable(CallbacksReceiver.class.getClassLoader()); if (r instanceof CallbacksReceiver) { groundy.mReceiver = (CallbacksReceiver) r; } else if (r != null) { //noinspection unchecked groundy.mReceiver = new CallbacksReceiver(groundyTask); Bundle bundle = new Bundle(); bundle.putParcelable(CallbacksReceiver.RECEIVER_PARCEL, groundy.mReceiver); r.send(CallbacksReceiver.ATTACH_RECEIVER_PARCEL, bundle); } } groundy.mArgs.putAll(source.readBundle()); groundy.mGroupId = source.readInt(); groundy.mAlreadyProcessed = source.readByte() == 1; //noinspection unchecked groundy.mGroundyClass = (Class) source.readSerializable(); groundy.mAllowNonUIThreadCallbacks = source.readByte() == 1; return groundy; } @Override public Groundy[] newArray(int size) { return new Groundy[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeSerializable(mGroundyTask); dest.writeLong(mId); dest.writeByte((byte) (mReceiver == null ? 0 : 1)); if (mReceiver != null) { dest.writeParcelable(mReceiver, flags); } dest.writeBundle(mArgs); dest.writeInt(mGroupId); dest.writeByte((byte) (mAlreadyProcessed ? 1 : 0)); dest.writeSerializable(mGroundyClass); dest.writeByte((byte) (mAllowNonUIThreadCallbacks ? 1 : 0)); } /** * Inserts a Boolean value into the mapping of this Bundle, replacing any existing value for the * given key. Either key or value may be null. * * @param key a String, or null * @param value a Boolean, or null */ public Groundy arg(String key, boolean value) { mArgs.putBoolean(key, value); return this; } /** * Inserts a byte value into the mapping of this Bundle, replacing any existing value for the * given key. * * @param key a String, or null * @param value a byte */ public Groundy arg(String key, byte value) { mArgs.putByte(key, value); return this; } /** * Inserts a char value into the mapping of this Bundle, replacing any existing value for the * given key. * * @param key a String, or null * @param value a char, or null */ public Groundy arg(String key, char value) { mArgs.putChar(key, value); return this; } /** * Inserts a short value into the mapping of this Bundle, replacing any existing value for the * given key. * * @param key a String, or null * @param value a short */ public Groundy arg(String key, short value) { mArgs.putShort(key, value); return this; } /** * Inserts an int value into the mapping of this Bundle, replacing any existing value for the * given key. * * @param key a String, or null * @param value an int, or null */ public Groundy arg(String key, int value) { mArgs.putInt(key, value); return this; } /** * Inserts a long value into the mapping of this Bundle, replacing any existing value for the * given key. * * @param key a String, or null * @param value a long */ public Groundy arg(String key, long value) { mArgs.putLong(key, value); return this; } /** * Inserts a float value into the mapping of this Bundle, replacing any existing value for the * given key. * * @param key a String, or null * @param value a float */ public Groundy arg(String key, float value) { mArgs.putFloat(key, value); return this; } /** * Inserts a double value into the mapping of this Bundle, replacing any existing value for the * given key. * * @param key a String, or null * @param value a double */ public Groundy arg(String key, double value) { mArgs.putDouble(key, value); return this; } /** * Inserts a String value into the mapping of this Bundle, replacing any existing value for the * given key. Either key or value may be null. * * @param key a String, or null * @param value a String, or null */ public Groundy arg(String key, String value) { mArgs.putString(key, value); return this; } /** * Inserts a CharSequence value into the mapping of this Bundle, replacing any existing value for * the given key. Either key or value may be null. * * @param key a String, or null * @param value a CharSequence, or null */ public Groundy arg(String key, CharSequence value) { mArgs.putCharSequence(key, value); return this; } /** * Inserts a Parcelable value into the mapping of this Bundle, replacing any existing value for * the given key. Either key or value may be null. * * @param key a String, or null * @param value a Parcelable object, or null */ public Groundy arg(String key, Parcelable value) { mArgs.putParcelable(key, value); return this; } /** * Inserts an array of Parcelable values into the mapping of this Bundle, replacing any existing * value for the given key. Either key or value may be null. * * @param key a String, or null * @param value an array of Parcelable objects, or null */ public Groundy arg(String key, Parcelable[] value) { mArgs.putParcelableArray(key, value); return this; } /** * Inserts an ArrayList value into the mapping of this Bundle, replacing any existing * value for the given key. Either key or value may be null. * * @param key a String, or null * @param value an ArrayList object, or null */ public Groundy addIntegerArrayList(String key, ArrayList value) { mArgs.putIntegerArrayList(key, value); return this; } /** * Inserts an ArrayList value into the mapping of this Bundle, replacing any existing * value for the given key. Either key or value may be null. * * @param key a String, or null * @param value an ArrayList object, or null */ public Groundy addStringArrayList(String key, ArrayList value) { mArgs.putStringArrayList(key, value); return this; } /** * Inserts an ArrayList value into the mapping of this Bundle, replacing any * existing value for the given key. Either key or value may be null. * * @param key a String, or null * @param value an ArrayList object, or null */ public Groundy arg(String key, ArrayList value) { mArgs.putCharSequenceArrayList(key, value); return this; } /** * Inserts a Serializable value into the mapping of this Bundle, replacing any existing value for * the given key. Either key or value may be null. * * @param key a String, or null * @param value a Serializable object, or null */ public Groundy arg(String key, Serializable value) { mArgs.putSerializable(key, value); return this; } /** * Inserts a boolean array value into the mapping of this Bundle, replacing any existing value * for * the given key. Either key or value may be null. * * @param key a String, or null * @param value a boolean array object, or null */ public Groundy arg(String key, boolean[] value) { mArgs.putBooleanArray(key, value); return this; } /** * Inserts a byte array value into the mapping of this Bundle, replacing any existing value for * the given key. Either key or value may be null. * * @param key a String, or null * @param value a byte array object, or null */ public Groundy arg(String key, byte[] value) { mArgs.putByteArray(key, value); return this; } /** * Inserts a short array value into the mapping of this Bundle, replacing any existing value for * the given key. Either key or value may be null. * * @param key a String, or null * @param value a short array object, or null */ public Groundy arg(String key, short[] value) { mArgs.putShortArray(key, value); return this; } /** * Inserts a char array value into the mapping of this Bundle, replacing any existing value for * the given key. Either key or value may be null. * * @param key a String, or null * @param value a char array object, or null */ public Groundy arg(String key, char[] value) { mArgs.putCharArray(key, value); return this; } /** * Inserts an int array value into the mapping of this Bundle, replacing any existing value for * the given key. Either key or value may be null. * * @param key a String, or null * @param value an int array object, or null */ public Groundy arg(String key, int[] value) { mArgs.putIntArray(key, value); return this; } /** * Inserts a long array value into the mapping of this Bundle, replacing any existing value for * the given key. Either key or value may be null. * * @param key a String, or null * @param value a long array object, or null */ public Groundy arg(String key, long[] value) { mArgs.putLongArray(key, value); return this; } /** * Inserts a float array value into the mapping of this Bundle, replacing any existing value for * the given key. Either key or value may be null. * * @param key a String, or null * @param value a float array object, or null */ public Groundy arg(String key, float[] value) { mArgs.putFloatArray(key, value); return this; } /** * Inserts a double array value into the mapping of this Bundle, replacing any existing value for * the given key. Either key or value may be null. * * @param key a String, or null * @param value a double array object, or null */ public Groundy arg(String key, double[] value) { mArgs.putDoubleArray(key, value); return this; } /** * Inserts a String array value into the mapping of this Bundle, replacing any existing value for * the given key. Either key or value may be null. * * @param key a String, or null * @param value a String array object, or null */ public Groundy arg(String key, String[] value) { mArgs.putStringArray(key, value); return this; } /** * Inserts a CharSequence array value into the mapping of this Bundle, replacing any existing * value for the given key. Either key or value may be null. * * @param key a String, or null * @param value a CharSequence array object, or null */ public Groundy arg(String key, CharSequence[] value) { mArgs.putCharSequenceArray(key, value); return this; } /** * Inserts a Bundle value into the mapping of this Bundle, replacing any existing value for the * given key. Either key or value may be null. * * @param key a String, or null * @param value a Bundle object, or null * @return itself */ public Groundy arg(String key, Bundle value) { mArgs.putBundle(key, value); return this; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy