package com.integreight.onesheeld.shields.controller; import android.app.Activity; import com.integreight.onesheeld.sdk.ShieldFrame; import com.integreight.onesheeld.R; import com.integreight.onesheeld.enums.UIShield; import com.integreight.onesheeld.shields.ControllerParent; import com.integreight.onesheeld.utils.Log; import android.content.Context; import android.os.Build; import android.os.Vibrator; import java.util.Vector; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Author: Mostafa Mahmoud * Email: mostafa_mahmoud@protonmail.com * Created on: 11/26/15 */ public class VibrationShield extends ControllerParent<VibrationShield> { private static final int NO_REPEAT = 65535 ; //65535 is the 2'complement of -1 private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor; private Vector<ScheduledFuture<?>> futureTasks; private VibrationShieldListener vibrationShieldListener; private Runnable onStart; private Runnable onPause; private Runnable onStop; private boolean isVibrating; private boolean isPaused; public interface VibrationShieldListener{ void onStart(); void onPause(); void onStop(); } public VibrationShield(){ super(); } public VibrationShield(Activity activity, String tag) { super(activity, tag); } public VibrationShield(Activity activity, String tag, boolean manageShieldSelectionFrameManually) { super(activity, tag, manageShieldSelectionFrameManually); } public void setVibrationShieldListener(VibrationShieldListener listener){ this.vibrationShieldListener = listener; } public boolean isVibrating(){ synchronized (this) { return isVibrating; } } public boolean isPaused(){ synchronized (this) { return isPaused; } } @Override public ControllerParent<VibrationShield> init(String tag) { scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1); if(Build.VERSION.SDK_INT >= 21) scheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true); onStart = new Runnable() { @Override public void run() { if (vibrationShieldListener != null) vibrationShieldListener.onStart(); synchronized (VibrationShield.this) { isVibrating = true; isPaused = false; } } }; onPause = new Runnable() { @Override public void run() { if (vibrationShieldListener != null) vibrationShieldListener.onPause(); synchronized (VibrationShield.this) { isVibrating = false; isPaused = true; } } }; onStop = new Runnable() { @Override public void run() { if (vibrationShieldListener != null) vibrationShieldListener.onStop(); synchronized (VibrationShield.this) { isVibrating = false; isPaused = false; } } }; futureTasks = new Vector<>(); return super.init(tag); } @Override public ControllerParent<VibrationShield> invalidate(SelectionAction selectionAction, boolean isToastable) { this.selectionAction = selectionAction; boolean hasVibrator; if(Build.VERSION.SDK_INT >=11) hasVibrator = ((Vibrator)getApplication().getSystemService(Context.VIBRATOR_SERVICE)) .hasVibrator(); else hasVibrator = getApplication().getSystemService(Context.VIBRATOR_SERVICE) != null; if(hasVibrator) { Log.d("Vibrator Availability","Available"); if (selectionAction !=null) selectionAction.onSuccess(); } else { Log.d("Unavailable Hardware", "Vibration"); if (selectionAction !=null) selectionAction.onFailure(); if (isToastable) activity.showToast(activity.getString(R.string.vibration_your_device_doesnt_support_this_hardware_toast)); } return super.invalidate(selectionAction, isToastable); } @Override public void onNewShieldFrameReceived(ShieldFrame frame) { if(frame.getShieldId() == UIShield.VIBRATION_SHIELD.getId()){ final Vibrator vibrator = (Vibrator)getApplication().getSystemService(Context.VIBRATOR_SERVICE); int period; stop(); switch (frame.getFunctionId()){ case 0x01: final byte[] receivedPattern = frame.getArgument(0); period = frame.getArgumentAsInteger(1); final long[] pattern = new long[receivedPattern.length/2]; for(int i = 0 ; i < pattern.length ; i++){ pattern[i] = ((receivedPattern[2*i+1] & 0xFFL) <<8 ) | (receivedPattern[i*2] & 0xFFL); } final long[] stoppingPattern = new long[receivedPattern.length/2]; stoppingPattern[0] = pattern[0]; for (int i = 1 ; i < stoppingPattern.length ; i++){ stoppingPattern[i] = stoppingPattern[i-1] + pattern[i]; } if (period == NO_REPEAT) { futureTasks.add(scheduledThreadPoolExecutor.schedule(new Runnable() { @Override public void run() { if (vibrationShieldListener != null) vibrationShieldListener .onPause(); synchronized (VibrationShield.this) { isVibrating = false; isPaused = true; } vibrator.vibrate(pattern, -1); } }, 0, TimeUnit.MILLISECONDS)); for(int i = 0 ; i < stoppingPattern.length-1 ; i++){ if(i%2 == 0){ futureTasks.add(scheduledThreadPoolExecutor.schedule(onStart, stoppingPattern[i], TimeUnit.MILLISECONDS)); } else{ futureTasks.add(scheduledThreadPoolExecutor.schedule(onPause, stoppingPattern[i], TimeUnit.MILLISECONDS)); } } futureTasks.add(scheduledThreadPoolExecutor.schedule(onStop, stoppingPattern[stoppingPattern.length-1], TimeUnit.MILLISECONDS)); } else { for (long i:pattern) period+= i; futureTasks.add(scheduledThreadPoolExecutor.scheduleWithFixedDelay( new Runnable() { @Override public void run() { if (vibrationShieldListener != null) vibrationShieldListener .onPause(); synchronized (VibrationShield.this) { isVibrating = false; isPaused = true; } vibrator.vibrate(pattern, -1); } }, 0, period, TimeUnit.MILLISECONDS)); futureTasks.add(scheduledThreadPoolExecutor.scheduleWithFixedDelay( new Runnable() { @Override public void run() { for (int i = 0; i < stoppingPattern.length - 1; i++) { if (i % 2 == 0) { futureTasks.add(scheduledThreadPoolExecutor.schedule(onStart, stoppingPattern[i], TimeUnit.MILLISECONDS)); } else { futureTasks.add(scheduledThreadPoolExecutor.schedule(onPause, stoppingPattern[i], TimeUnit.MILLISECONDS)); } } futureTasks.add(scheduledThreadPoolExecutor.schedule(onPause, stoppingPattern[stoppingPattern.length - 1], TimeUnit.MILLISECONDS)); } },0,period,TimeUnit.MILLISECONDS)); } break; case 0x02: final int duration = frame.getArgumentAsInteger(0); period = frame.getArgumentAsInteger(1); if (period == NO_REPEAT) { futureTasks.add(scheduledThreadPoolExecutor.schedule(new Runnable() { @Override public void run() { if (vibrationShieldListener != null) vibrationShieldListener .onStart(); synchronized (VibrationShield.this) { isVibrating = true; isPaused = false; } vibrator.vibrate(duration); } }, 0, TimeUnit.MILLISECONDS)); futureTasks.add(scheduledThreadPoolExecutor.schedule(onStop, duration,TimeUnit.MILLISECONDS)); } else { period += duration; futureTasks.add(scheduledThreadPoolExecutor.scheduleWithFixedDelay( new Runnable() { @Override public void run() { if (vibrationShieldListener != null) vibrationShieldListener .onStart(); synchronized (VibrationShield.this) { isVibrating = true; isPaused = false; } vibrator.vibrate(duration); } }, 0, period, TimeUnit.MILLISECONDS)); futureTasks.add(scheduledThreadPoolExecutor.scheduleWithFixedDelay(onPause, duration,period,TimeUnit.MILLISECONDS)); } break; case 0x03: stop(); break; } } } public void stop(){ if (vibrationShieldListener != null) vibrationShieldListener.onStop(); synchronized (VibrationShield.this) { isVibrating = false; isPaused = false; } ((Vibrator)getApplication().getSystemService(Context.VIBRATOR_SERVICE)).cancel(); for (ScheduledFuture task:futureTasks) task.cancel(true); futureTasks.clear(); } @Override public void reset() { ((Vibrator)getApplication().getSystemService(Context.VIBRATOR_SERVICE)).cancel(); if (!scheduledThreadPoolExecutor.isShutdown()) scheduledThreadPoolExecutor.shutdown(); futureTasks.clear(); } }