/*
VibrationExecutor
Copyright (c) 2016 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.linking.linking.profile;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* Support Class for Vibration Profile pattern parameter.
*
* @author NTT DOCOMO, INC.
*/
final class VibrationExecutor {
public interface VibrationControllable {
void changeVibration(boolean isOn, CompleteListener listener);
}
public interface CompleteListener {
void onComplete();
}
private VibrationControllable mListener;
private ScheduledExecutorService mPatternService = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture mLatestScheduledFuture;
private Queue<Long> mPatternQueue = new ConcurrentLinkedQueue<Long>();
private boolean mIsOn = true;
private int mLastIdentifier = 0;
public VibrationExecutor() {
}
public synchronized void setVibrationControllable(VibrationControllable controllable) {
mListener = controllable;
}
public synchronized void start(long[] flashing) {
cancelSchedule();
updateQueue(flashing);
final VibrationControllable listener = getVibrationControllable();
if (listener == null) {
return;
}
mLastIdentifier++;
final int identifier = mLastIdentifier;
//Turn on vibration at first.
listener.changeVibration(true, new CompleteListener() {
@Override
public void onComplete() {
if (!checkIdentifier(identifier)) {
return;
}
Long firstInterval = getNextInterval();
//Start first waiting during vibrating.
schedule(new Runnable() {
@Override
public void run() {
onFinishFirstVibration();
}
}, firstInterval);
}
});
}
private synchronized void onFinishFirstVibration() {
//Turn off vibration and start next waiting.
setOn(false);
Long interval = getNextInterval();
controlVibration(isOn(), new Runnable() {
@Override
public void run() {
next(this);
}
}, interval);
}
private synchronized void next(Runnable runnable) {
final Long interval = getNextInterval();
if (interval == null) {
onFinish();
return;
}
VibrationControllable listener = getVibrationControllable();
if (listener == null) {
onFinish();
return;
}
toggleOnOff();
controlVibration(isOn(), runnable, interval);
}
private synchronized void controlVibration(boolean isOn, final Runnable runnable, final Long interval) {
mLastIdentifier++;
final int identifier = mLastIdentifier;
VibrationControllable listener = getVibrationControllable();
if (listener == null) {
onFinish();
return;
}
listener.changeVibration(isOn, new CompleteListener() {
@Override
public void onComplete() {
if (!checkIdentifier(identifier)) {
return;
}
if (isLastPattern()) {
onFinish();
} else {
schedule(runnable, interval);
}
}
});
}
private synchronized boolean isLastPattern() {
return mPatternQueue.size() == 0;
}
private synchronized boolean checkIdentifier(int identifier) {
return identifier == mLastIdentifier;
}
private synchronized void onFinish() {
mListener = null;
mLatestScheduledFuture = null;
}
private synchronized VibrationControllable getVibrationControllable() {
return mListener;
}
private synchronized void cancelSchedule() {
if (mLatestScheduledFuture != null && !mLatestScheduledFuture.isCancelled()) {
mLatestScheduledFuture.cancel(false);
}
}
private synchronized void schedule(Runnable runnable, long interval) {
mLatestScheduledFuture = mPatternService.schedule(runnable, interval, TimeUnit.MILLISECONDS);
}
private synchronized void updateQueue(long[] flashing) {
mPatternQueue.clear();
for (long value : flashing) {
mPatternQueue.add(value);
}
}
private synchronized Long getNextInterval() {
return mPatternQueue.poll();
}
private synchronized void setOn(boolean isOn) {
mIsOn = isOn;
}
private synchronized boolean isOn() {
return mIsOn;
}
private synchronized void toggleOnOff() {
mIsOn = !mIsOn;
}
}