|
import android.app.Application; |
|
import android.content.Context; |
|
import android.hardware.SensorManager; |
|
import android.os.Handler; |
|
import android.os.Looper; |
|
import android.support.annotation.NonNull; |
|
import android.support.annotation.Nullable; |
|
|
|
import java.lang.reflect.Constructor; |
|
import java.lang.reflect.InvocationHandler; |
|
import java.lang.reflect.InvocationTargetException; |
|
import java.lang.reflect.Method; |
|
import java.lang.reflect.Proxy; |
|
|
|
/** |
|
* Created by tagakov on 16.09.15. |
|
* Hidden button for debugging purposes. |
|
* Invoking provided function on device shaking |
|
* |
|
* Should be used with: |
|
* |
|
* debugCompile 'com.squareup:seismic:1.0.2' |
|
* |
|
* Typical use is: |
|
* @<code> |
|
* if (BuildConfig.DEBUG) { |
|
* DevButton.init(getApplicationContext()).onShake(new Runnable() { |
|
* @Override |
|
* public void run() { |
|
* //do something |
|
* } |
|
* }); |
|
* } |
|
* </code> |
|
*/ |
|
@SuppressWarnings("TryWithIdenticalCatches") |
|
public enum DevButton { |
|
BUTTON; |
|
|
|
private static final long SHAKING_THRESHOLD_MS = 500; |
|
|
|
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper()); |
|
|
|
private Application application; |
|
private volatile Runnable onShakeRun; |
|
|
|
private volatile boolean shakerInitialized; |
|
|
|
|
|
@NonNull |
|
public DevButton init(Application application) { |
|
this.application = application; |
|
return this; |
|
} |
|
|
|
/** |
|
* Do some work after device shaking |
|
* |
|
* @param run - function that would be invoked on shake detection |
|
* @return - self for chaining |
|
*/ |
|
public synchronized DevButton onShake(@Nullable Runnable run) { |
|
if (application == null) { |
|
throw new IllegalStateException("The DevButton is not initialized! Please initialize it with Application context!"); |
|
} |
|
onShakeRun = run; |
|
enableShakeDetection(); |
|
return this; |
|
} |
|
|
|
private synchronized void enableShakeDetection() { |
|
if (shakerInitialized) return; |
|
|
|
SensorManager sensorManager = (SensorManager) application.getSystemService(Context.SENSOR_SERVICE); |
|
if (sensorManager == null) return; //nothing to do here |
|
|
|
try { |
|
Class<?> shakersListenerClass = Class.forName("com.squareup.seismic.ShakeDetector$Listener"); |
|
Class<?> shakerClass = Class.forName("com.squareup.seismic.ShakeDetector"); |
|
|
|
Constructor<?> constructor = shakerClass.getConstructor(shakersListenerClass); |
|
Method shakerStart = shakerClass.getMethod("start", SensorManager.class); |
|
|
|
Object listener = Proxy.newProxyInstance(shakersListenerClass.getClassLoader(), new Class[]{shakersListenerClass}, new InvocationHandler() { |
|
long lastInvoked; |
|
@Override |
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
|
if (System.currentTimeMillis() - lastInvoked > SHAKING_THRESHOLD_MS) { |
|
if (onShakeRun != null) { |
|
mainThreadHandler.postDelayed(onShakeRun, 0); |
|
} |
|
lastInvoked = System.currentTimeMillis(); |
|
} |
|
return null; |
|
} |
|
}); |
|
|
|
shakerStart.invoke(constructor.newInstance(listener), sensorManager); |
|
shakerInitialized = true; |
|
} catch (ClassNotFoundException ignore) { |
|
//Ok we can't do the fancy stuff for debugging |
|
} catch (NoSuchMethodException ignore) { |
|
//Ok we can't do the fancy stuff for debugging |
|
} catch (InvocationTargetException ignore) { |
|
//Ok we can't do the fancy stuff for debugging |
|
} catch (InstantiationException ignore) { |
|
//Ok we can't do the fancy stuff for debugging |
|
} catch (IllegalAccessException ignore) { |
|
//Ok we can't do the fancy stuff for debugging |
|
} |
|
} |
|
} |