Sunday, May 1, 2011

Orientation Change

Problem
I wanted to lock device's orientation to portrait and catch event when user will turn device to landscape and back to portrait orientation.

Solution
I found two ways of solving described problem.
1. By use SensorEventListener
2. By use OrientationEventListener

On my opinion 2. solution is "more natural" based on problem. But sometimes you need to handle it in other way. So it is good to know both aspects.

SensorEventListener
With SensorEventListener you have to implement a interface with same name.
That method gets sensor of type accelerometer which shows G-forces on every direction (x, y, z). By that you can get values from each direction. Gx + Gy + Gz should always be = G (8,91).
At first we have to get SensorManager and accelerometer sensor.
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mAccelerometerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

Because we want to write code which is battery-aware we receive data from sensors only after onResume happens and stop receiving data after onPause happens.

@Override
protected void onPause() {
 if (DEBUG) {
  Log.d(TAG, "onPause()");
 }
 super.onPause();
 mSensorManager.unregisterListener(this);
}

@Override
protected void onResume() {
 if (DEBUG) {
  Log.d(TAG, "onResume()");
 }
 super.onResume();
 mSensorManager.registerListener(this, mAccelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);
}

And the "main" method goes here
@Override
public void onSensorChanged(SensorEvent event) {
 if (DEBUG) {
  Log.d(TAG, "onSensorChanged()");
 }
 if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
  mAccelerometerSensorValues = event.values;
  if (DEBUG) {
   Log.d(TAG, "Gx = " + mAccelerometerSensorValues[0]);
   Log.d(TAG, "Gy = " + mAccelerometerSensorValues[1]);
   Log.d(TAG, "Gz = " + mAccelerometerSensorValues[2]);
  }

  float gx = mAccelerometerSensorValues[0];
  float gy = mAccelerometerSensorValues[1];

  if (gx > G_THRESHOLD) {
   if (mOrientation != Configuration.ORIENTATION_PORTRAIT) {
    mOrientation = Configuration.ORIENTATION_PORTRAIT;
    if (DEBUG) {
     Log.d(TAG, "orientation = " + mOrientation);
    }
    RotateAnimation rotateAnimation = new RotateAnimation(0, 90, mMainText.getWidth() / 2, mMainText.getHeight() / 2);
    rotateAnimation.setDuration(500);
    rotateAnimation.setFillAfter(true);
    mMainText.startAnimation(rotateAnimation);
   }
  } else if (gy > G_THRESHOLD) {
   if (mOrientation != Configuration.ORIENTATION_LANDSCAPE) {
    mOrientation = Configuration.ORIENTATION_LANDSCAPE;
    if (DEBUG) {
     Log.d(TAG, "orientation = " + mOrientation);
    }
    RotateAnimation rotateAnimation = new RotateAnimation(90, 0, mMainText.getWidth() / 2, mMainText.getHeight() / 2);
    rotateAnimation.setDuration(500);
    rotateAnimation.setFillAfter(true);
    mMainText.startAnimation(rotateAnimation);
   }
  }
 }
}
Here we get all three values (Gx, Gy and Gz) and compare it with threshold. Based on this values we decide on what orientation is device turned. If orientation changes we rotate text to proper position.

OrientationEventListener
Here we implement OrientationEventListener. This interface has only one method which gives us current rotation of the device in degrees. With that parameter we can decide how much degrees suitable to change orientation.
But at first again a little bit of administration. Within onCreate method we will do the main part = implementation of interface.
Again methods onPause and onResume take care of disabling/enabling listener.
@Override
protected void onPause() {
 if (DEBUG) {
  Log.d(TAG, "onPause()");
 }
 super.onPause();
 mMyOrientationEventListener.disable();
}

@Override
protected void onResume() {
 if (DEBUG) {
  Log.d(TAG, "onResume()");
 }
 super.onResume();
 if (mMyOrientationEventListener.canDetectOrientation()) {
  Toast.makeText(this, "Can DetectOrientation = Enabling Listener", Toast.LENGTH_LONG).show();
  mMyOrientationEventListener.enable();
 } else {
  Toast.makeText(this, "Can't DetectOrientation", Toast.LENGTH_LONG).show();
  finish();
 }
}
Implementation of interface look like
mMyOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
 @Override
 public void onOrientationChanged(int orientation) {
  int newScreenOrientation = getScreenOrientation(orientation);
  if (DEBUG) {
   Log.d(TAG, "Orientation: " + orientation + ", newScreenOrientation: " + newScreenOrientation + ", currentScreenOrientation: " + mCurrentOrientation);
  }
  if (newScreenOrientation != mCurrentOrientation) {
   if (mCurrentOrientation == Configuration.ORIENTATION_PORTRAIT) {
    RotateAnimation rotateAnimation = new RotateAnimation(0, 90, mMainText.getWidth() / 2, mMainText.getHeight() / 2);
    rotateAnimation.setDuration(500);
    rotateAnimation.setFillAfter(true);
    mMainText.startAnimation(rotateAnimation);
   } else if (mCurrentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
    RotateAnimation rotateAnimation = new RotateAnimation(90, 0, mMainText.getWidth() / 2, mMainText.getHeight() / 2);
    rotateAnimation.setDuration(500);
    rotateAnimation.setFillAfter(true);
    mMainText.startAnimation(rotateAnimation);
   }
   mCurrentOrientation = newScreenOrientation;
  }
 }
};
getScreenOrientation gets proper orientation constant based on current device rotating angle.

Full code can be found here

Saturday, April 30, 2011

About me & myself

I'm Boris Malensek and I come from Slovenia. Wh00t? Slovenia? Where is this? Slovakia?
Eh. It does not matter. I'm an Android developer @SiS Software. My job is to develop apps and games 4 Android and in everyday job I meet with some interesting issues which I have to solve. Beside Android I'm in love with Java!
For some issues I wasn't able to find any suitable solution so I decided to start writing this blog and maybe I'll help some of you.
If you have any real issues and you really really really need a solution for them please let me know.
You can do it on several ways:

  • very old-stylish by email boris (dot) malensek (at) gmail (dot) com
  • a little bit fancier by skype (boris (dot) malensek) or by gtalk (same as my email address)
  • for "young" people by facebook or by twitter