package app.create.rpg; import java.util.Iterator; import java.util.LinkedList; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.ResultReceiver; import android.support.v4.app.NotificationCompat; import android.util.Log; import android.util.SparseArray; import app.create.rpg.task.Task; public class ServiceFileTask extends Service { public static final String ACTION_SET_RECEIVER = "app.create.rpg.ACTION_SET_RECEIVER", ACTION_PUSH_TASK = "app.create.rpg.ACTION_PUSH_TASK", ACTION_CANCEL_TASK = "app.create.rpg.ACTION_CANCEL_TASK", ACTION_STOP_QUEUE = "app.create.rpg.ACTION_STOP_QUEUE"; public static class MyQueue extends Thread implements Parcelable { LinkedList<Task> mTaskQueue; Task mCurrent; int mNextId, mQueueId; ServiceFileTask mParent; NotificationCompat.Builder mBuilder; public MyQueue() { mTaskQueue = new LinkedList<Task>(); mCurrent = null; mNextId = 0; mQueueId = 0; } MyQueue(ServiceFileTask parent, int queueId) { this(); mQueueId = queueId; mParent = parent; } public MyQueue(Parcel parcel) { final ClassLoader cl = getClass().getClassLoader(); parcel.readList(mTaskQueue = new LinkedList<Task>(), cl); mCurrent = parcel.readParcelable(cl); mNextId = parcel.readInt(); mQueueId = parcel.readInt(); } public synchronized String toString() { if (mCurrent == null) return mTaskQueue.size() > 0 ? "[pending]" : "[No task]"; return mCurrent.getMessage(); } public synchronized float getProgress() { if (mCurrent == null) return 0.0f; return mCurrent.getProgress(); } public synchronized void pushTask(Task task) { task.setService(mParent, mNextId++, mQueueId); mTaskQueue.add(task); } public synchronized void stopTask(int id) { if (id == -1 || mCurrent.getTaskId() == id) interrupt(); else { for (Iterator<Task> it = mTaskQueue.iterator(); it.hasNext();) { Task t = it.next(); if (t.getTaskId() == id) { it.remove(); break; } } } } public void destroyQueue() { mTaskQueue = null; mCurrent = null; interrupt(); } public void run() { try { Log.d("CreateRPG", "Start Queue " + mQueueId); while (!(mTaskQueue == null || mParent.mStop)) { try { synchronized (this) { mCurrent = mTaskQueue.poll(); } if (mCurrent == null || mParent.mStop) { Log.d("CreateRPG", "Queue " + mQueueId + ", Out of task queue"); break; } if (mParent.mStop) break; if (interrupted()) continue; Log.d("CreateRPG", "Queue " + mQueueId + ", Start task " + mCurrent); mParent.onTaskUpdate(); mCurrent.run(); mParent.onTaskUpdate(); Log.d("CreateRPG", "Queue " + mQueueId + ", End task " + mCurrent); synchronized (this) { mCurrent = null; } Thread.sleep(100); } catch (InterruptedException e) { } } } finally { synchronized (mParent.mTasks) { SparseArray<MyQueue> list = mParent.mTasks; list.delete(list.indexOfValue(this)); } mParent.deleteQueue(this); Log.d("CreateRPG", "End Queue " + mQueueId); } } @Override public int describeContents() { // TODO Auto-generated method stub return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeList(mTaskQueue); dest.writeParcelable(mCurrent, 0); dest.writeInt(mNextId); dest.writeInt(mQueueId); } public static final MyQueue EMPTY = new MyQueue(); public static final Creator<MyQueue> CREATOR = new Creator<ServiceFileTask.MyQueue>() { public MyQueue[] newArray(int size) { return new MyQueue[size]; } public MyQueue createFromParcel(Parcel source) { return new MyQueue(source); } }; } public class MyBinder extends Binder { public SparseArray<MyQueue> getTasks() { return mTasks; } } private SparseArray<MyQueue> mTasks; private final IBinder mBinder = new MyBinder(); private boolean mStop; private ResultReceiver mReceiver; private NotificationCompat.Builder mBuilder, mNotifyComplete; private PendingIntent mPending; private NotificationManager mNM; public void onTaskUpdate() { if (mReceiver != null) mReceiver.send(0, null); } public PendingIntent getTaskDialog() { return mPending; } public void onTaskUpdate(Task task) { onTaskUpdate(); float pro = task.getProgress(); if (pro < 0f || pro > 1f) mBuilder.setProgress(0, 0, true); else mBuilder.setProgress(10000, Math.round(task.getProgress() * 10000f), false); mNM.notify(getClass().getName(), R.string.task_complete + task.getQueueId(), mBuilder .setContentTitle(new StringBuilder().append(Math.round(task.getProgress() * 100f)).append("% done")) .setContentText(task.getMessage()).build()); } public void deleteQueue(MyQueue queue) { onTaskUpdate(); mNM.cancel(getClass().getName(), R.string.task_complete + queue.mQueueId); mNM.notify(getClass().getName(), R.string.task_complete + queue.mQueueId, mNotifyComplete .setTicker(getText(R.string.task_complete)) .setContentTitle(getText(R.string.task_complete)).build()); } @Override public void onCreate() { super.onCreate(); mStop = false; mTasks = new SparseArray<MyQueue>(); mReceiver = null; mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); mPending = PendingIntent.getActivity(this, 0, new Intent(this, ActivityTaskDialog.class), 0); CharSequence ticker = getText(R.string.app_name); mBuilder = new NotificationCompat.Builder(this) .setAutoCancel(false).setOngoing(true).setOnlyAlertOnce(true) .setTicker(ticker) .setContentIntent(mPending) .setSmallIcon(R.drawable.ic_launcher); mNotifyComplete = new NotificationCompat.Builder(this) .setAutoCancel(true).setOngoing(false) .setContentIntent(mPending) .setSmallIcon(R.drawable.ic_launcher); } @Override public void onDestroy() { super.onDestroy(); mStop = true; } public void handleCommand(Intent intent) { if (intent == null) { return; } Log.d("CreateRPG", "ServiceFileTask Command : " + intent.getAction()); long time = System.currentTimeMillis(); intent.getExtras().setClassLoader(getClass().getClassLoader()); try { String action = intent.getAction(); if (action.equals(ACTION_SET_RECEIVER)) { mReceiver = intent.getParcelableExtra("receiver"); return; } int queueid = intent.getIntExtra("queueid", -1); MyQueue queue; if (queueid == -1) { if (action.equals(ACTION_STOP_QUEUE)) { synchronized (mTasks) { for (int i = 0; i < mTasks.size(); ++i) { mTasks.valueAt(i).destroyQueue(); } mTasks.clear(); } return; } queueid = 0; // look for a empty queue index synchronized (mTasks) { for (int i = 0; i < mTasks.size(); ++i) { if (mTasks.keyAt(i) > queueid) break; queueid = mTasks.keyAt(i) + 1; } queue = mTasks.get(queueid); } if (queueid < 0) { // overflow return; } } else queue = mTasks.get(queueid); if (action.equals(ACTION_STOP_QUEUE)) { if (queue != null) { synchronized (mTasks) { mTasks.delete(queueid); } mNM.cancel(getClass().getName(), R.string.task_complete + queueid); queue.destroyQueue(); } return; } if (queue == null) { synchronized (mTasks) { mTasks.put(queueid, queue = new MyQueue(this, queueid)); } } if (action.equals(ACTION_PUSH_TASK)) { Parcelable[] arr = intent.getParcelableArrayExtra("tasks"); if (arr != null) { Log.d("CreateRPG", "Size : " + arr.length); for (Parcelable t : arr) { Log.d("CreateRPG", t == null ? "null" : t.toString()); if (t != null) queue.pushTask((Task) t); } } else queue.pushTask((Task) intent.getParcelableExtra("task")); if (!queue.isAlive()) queue.start(); startActivity(new Intent(this, ActivityTaskDialog.class).setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)); } else if (action.equals(ACTION_CANCEL_TASK)) { queue.stopTask(intent.getIntExtra("taskid", -1)); } } finally { Log.d("CreateRPG", "ServiceFileTask Command : " + intent.getAction() + ", Finished in : " + (System.currentTimeMillis() - time) / 1000.0f + " sec"); onTaskUpdate(); } } @Override public void onStart(Intent intent, int startId) { handleCommand(intent); } @Override public int onStartCommand(Intent intent, int flags, int startId) { handleCommand(intent); return START_NOT_STICKY; } @Override public IBinder onBind(Intent intent) { return mBinder; } }