/*******************************************************************************
* Copyright 2013 Ray Tsang
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package jdeferred.android;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import org.jdeferred.AlwaysCallback;
import org.jdeferred.Deferred;
import org.jdeferred.DoneCallback;
import org.jdeferred.FailCallback;
import org.jdeferred.ProgressCallback;
import org.jdeferred.Promise;
import org.jdeferred.impl.DeferredObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import jdeferred.android.annotation.ExecutionScope;
public class AndroidDeferredObject<D, F, P> extends DeferredObject<D, F, P> {
private static final InternalHandler sHandler = new InternalHandler();
private static final int MESSAGE_POST_DONE = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static final int MESSAGE_POST_FAIL = 0x3;
private static final int MESSAGE_POST_ALWAYS = 0x4;
final protected Logger log = LoggerFactory
.getLogger(AndroidDeferredObject.class);
private final AndroidExecutionScope defaultAndroidExecutionScope;
public AndroidDeferredObject(Promise<D, F, P> promise) {
this(promise, AndroidExecutionScope.UI);
}
public AndroidDeferredObject(Promise<D, F, P> promise,
AndroidExecutionScope defaultAndroidExecutionScope) {
this.defaultAndroidExecutionScope = defaultAndroidExecutionScope;
promise.done(new DoneCallback<D>() {
@Override
public void onDone(D result) {
AndroidDeferredObject.this.resolve(result);
}
}).progress(new ProgressCallback<P>() {
@Override
public void onProgress(P progress) {
AndroidDeferredObject.this.notify(progress);
}
}).fail(new FailCallback<F>() {
@Override
public void onFail(F result) {
AndroidDeferredObject.this.reject(result);
}
});
}
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public void handleMessage(Message msg) {
CallbackMessage<?, ?, ?, ?> result = (CallbackMessage<?, ?, ?, ?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_DONE:
((DoneCallback) result.callback).onDone(result.resolved);
break;
case MESSAGE_POST_PROGRESS:
((ProgressCallback) result.callback)
.onProgress(result.progress);
break;
case MESSAGE_POST_FAIL:
((FailCallback) result.callback).onFail(result.rejected);
break;
case MESSAGE_POST_ALWAYS:
((AlwaysCallback) result.callback).onAlways(result.state,
result.resolved, result.rejected);
break;
}
}
}
protected void triggerDone(DoneCallback<D> callback, D resolved) {
if (determineAndroidExecutionScope(callback) == AndroidExecutionScope.UI) {
executeInUiThread(MESSAGE_POST_DONE, callback, State.RESOLVED,
resolved, null, null);
} else {
super.triggerDone(callback, resolved);
}
};
protected void triggerFail(FailCallback<F> callback, F rejected) {
if (determineAndroidExecutionScope(callback) == AndroidExecutionScope.UI) {
executeInUiThread(MESSAGE_POST_FAIL, callback, State.REJECTED,
null, rejected, null);
} else {
super.triggerFail(callback, rejected);
}
};
protected void triggerProgress(ProgressCallback<P> callback, P progress) {
if (determineAndroidExecutionScope(callback) == AndroidExecutionScope.UI) {
executeInUiThread(MESSAGE_POST_PROGRESS, callback, State.PENDING,
null, null, progress);
} else {
super.triggerProgress(callback, progress);
}
};
protected void triggerAlways(AlwaysCallback<D, F> callback, State state,
D resolve, F reject) {
if (determineAndroidExecutionScope(callback) == AndroidExecutionScope.UI) {
executeInUiThread(MESSAGE_POST_ALWAYS, callback, state, resolve,
reject, null);
} else {
super.triggerAlways(callback, state, resolve, reject);
}
};
protected <Callback> void executeInUiThread(int what, Callback callback,
State state, D resolve, F reject, P progress) {
Message message = sHandler.obtainMessage(what,
new CallbackMessage<Callback, D, F, P>(this, callback, state,
resolve, reject, progress));
message.sendToTarget();
}
protected AndroidExecutionScope determineAndroidExecutionScope(Class<?> clazz, String methodName, Class<?> ... arguments) {
ExecutionScope scope = null;
if (methodName != null) {
try {
Method method = clazz.getMethod(methodName, arguments);
scope = method.getAnnotation(ExecutionScope.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
if (scope == null) {
scope = clazz.getAnnotation(ExecutionScope.class);
}
return scope == null ? defaultAndroidExecutionScope : scope.value();
}
protected AndroidExecutionScope determineAndroidExecutionScope(Object callback) {
AndroidExecutionScope scope = null;
if (callback instanceof AndroidExecutionScopeable) {
scope = ((AndroidExecutionScopeable) callback).getExecutionScope();
} else if (callback instanceof DoneCallback) {
return determineAndroidExecutionScope(callback.getClass(), "onDone", Object.class);
} else if (callback instanceof FailCallback) {
return determineAndroidExecutionScope(callback.getClass(), "onFail", Object.class);
} else if (callback instanceof ProgressCallback) {
return determineAndroidExecutionScope(callback.getClass(), "onProgress", Object.class);
} else if (callback instanceof AlwaysCallback) {
return determineAndroidExecutionScope(callback.getClass(), "onAlways", State.class, Object.class, Object.class);
}
return scope == null ? defaultAndroidExecutionScope : scope;
}
@SuppressWarnings("rawtypes")
private static class CallbackMessage<Callback, D, F, P> {
final Deferred deferred;
final Callback callback;
final D resolved;
final F rejected;
final P progress;
final State state;
CallbackMessage(Deferred deferred, Callback callback, State state,
D resolved, F rejected, P progress) {
this.deferred = deferred;
this.callback = callback;
this.state = state;
this.resolved = resolved;
this.rejected = rejected;
this.progress = progress;
}
}
}