ページ

2013/12/16

[Android]端末が振られたことを加速度センサーから検出する

せっかく SensorFragment を作ったんだから何かサンプルを作りたかったのだけれど
ようやくそれっぽいものができたので公開してみる。
[Android]加速度センサーのノイズ除去とSensorFragment | DevAchieve
もうちょっと面白みがあると良いんだけど何も思いつかなかったから
android.hlidskialf.com/blog/code/android-shake-detection-listener参考に
パクってシェイクを検知する Activity を作った。
SensorActivity.java | wada811/AndroidLibrary-wada811

public class SensorActivity extends FragmentActivity implements SensorCallbackProvider {

    final SensorActivity self = this;

    private static final int SPEED_THRESHOLD = 25;
    private static final int SHAKE_TIMEOUT = 500;
    private static final int SHAKE_DURATION = 1000;
    private static final int SHAKE_COUNT = 3;
    private int mShakeCount = 0;
    private long mLastTime = 0;
    private long mLastAccel = 0;
    private long mLastShake = 0;
    private float mLastX, mLastY, mLastZ = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);

        ArrayList<Integer> sensorTypes = new ArrayList<Integer>();
        sensorTypes.add(Sensor.TYPE_LINEAR_ACCELERATION);
        SensorFragment sensorFragment = SensorFragment.newInstance(sensorTypes);
        getSupportFragmentManager().beginTransaction().add(sensorFragment, SensorFragment.TAG).commit();
    }

    @Override
    public SensorCallback getSensorCallback(){
        return new SensorCallback(){
            @Override
            public void onLinearAccelerationSensorChanged(float x, float y, float z){
                LogUtils.v("x: " + String.format("%+01.2f", x) + ", y: " + String.format("%+01.2f", y) + ", z: " + String.format("%+01.2f", z));
                boolean isShaked = detectShake(x, y, z);
                if(isShaked){
                    AndroidUtils.showToast(self, "shaked!");
                }
            }
        };
    }

    public boolean detectShake(float x, float y, float z){
        boolean isShaked = false;
        // シェイク時間のチェック
        long now = System.currentTimeMillis();
        if(mLastTime == 0){
            mLastTime = now;
        }
        // SHAKE_TIMEOUT までに次の加速を検知しなかったら mShakeCount をリセット
        if(now - mLastAccel > SHAKE_TIMEOUT){
            mShakeCount = 0;
        }
        // 速度を算出する
        long diff = now - mLastTime;
        float speed = Math.abs(x + y + z - mLastX - mLastY - mLastZ) / diff * 10000;
        LogUtils.d("speed: " + speed);
        if(speed > SPEED_THRESHOLD){
            // mShakeCount の加算、SHAKE_COUNT を超えているかのチェック
            // 最後のシェイク時間から SHAKE_DURATION 経過しているかチェック
            if(++mShakeCount >= SHAKE_COUNT && now - mLastShake > SHAKE_DURATION){
                mLastShake = now;
                mShakeCount = 0;
                isShaked = true;
            }
            // SPEED_THRESHOLD を超える速度を検出した時刻をセット
            mLastAccel = now;
        }
        mLastTime = now;
        mLastX = x;
        mLastY = y;
        mLastZ = z;
        return isShaked;
    }

}
それぞれの閾値が適切かどうかはわからないです。
歩いていて手に持った端末が振られたことを検知していたから Xperia A にはちょうどいいかもしれません。
<ポイント>
1.機種によって加速度センサーの閾値が違う。
2.機種によって加速度センサーの搭載位置が違う。
→同じくらいの強さで振っても機種によって取得できる値がまったく違う。
3.端末持つ位置でも当然変わってくる。
加速度センサーを利用した自社Androidアプリ『鮭 – Shake』をリリース!! | R-Labs
これを調整するの難しそうです。
収集した実測値の標準偏差を算出して調整幅のMAX値とMIN値を決定しています。
加速度センサーを利用した自社Androidアプリ『鮭 – Shake』をリリース!! | R-Labs
これが一番良い対処法かもしれません。
このへんは調整だからコスパと重要度と精度を天秤にかけてあげなければいけなさそうです。