lunes, 30 de abril de 2012

Realidad Aumentada en Android. Deshabilitar auto rotación

He visto que cuando se rota el móvil, la actividad de la cámara se cae. Por lo que he estado investigando, cuando se rota el móvil se destruye la actividad y se vuelve a crear, por lo que el acceso a la cámara también se cae. La única solución que he encontrado es deshabilitar la autorotación desde el manifest de la aplicación

...
        <activity
            android:name=".RealidadAumentadaActivity"
            android:label="@string/app_name" 
            android:screenOrientation="landscape"
            android:configChanges="orientation|keyboardHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        ....


La propiedad dentro del manifest que configChanges, que indica para que eventos de la actividad, esta no se destruye. Además, se indica que la aplicación se lance en modo landscape, mediante la propiedad screenOrientation

Actualización: Para que no aparezca la barra del título de la aplicación, se puede modificar el manifest de la aplicación de nuevo, añadiendo la propiedad android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" a la actividad principal RealidadAumentadaActivity

...
      
        <activity
            android:name=".RealidadAumentadaActivity"
            android:label="@string/app_name" 
            android:screenOrientation="landscape"
            android:configChanges="orientation|keyboardHidden"
            android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
  ....


jueves, 26 de abril de 2012

Realidad Aumentada en Android. Acceso a la cámara.

Realidad Aumentada es la habilidad de superponer puntos de información sobre la vista ofrecida por la cámara del dispositivo móvil. La Realidad Aumentada permite aumentar la información ofrecida por la cámara del dispositivo móvil, e interacturar con sensores y dispositivos como el magnetómetro, acelerómetro o geolocalización y ofrecer esta información añadida junto con la imagen ofrecida por la cámara. A la hora de desarrollar alguna aplicación de realidad aumentada, la base es acceder a los dispositivos y sensores del teléfono móvil; cámara, mágnetometro y acelerómetro y GPS.


Acceso a la vista de la cámara


El primer paso para implementar un entorno de realidad aumentada es acceder a la cámara para tener una vista del entorno real en el que se ofrece la funcionalidad deseada. Se debe dar permiso a la aplicación para acceder a la cámara del dispositivo, para lo que se modifica el Manifest de la aplicación


<application android:icon="@drawable/icon" 
                android:label="@string/app_name"
                android:debuggable="true">
...
<uses-permission android:name="android.permission.CAMERA" />
...
</application>



Lo siguiente es tener acceso a la vista de superficie de la camara sobre la que se va a pintar las imágenes capturadas. por la cámara. Para esto se extiende la clase SurfaceView. Se debe acceder a la clase SurfaceHolder para acceder y controlar la superficie subyacente, asi mismo, se debe implementar la clase interna Callback de la clase SurfaceHolder para recibir información acerca de cambios en la superficie. La superficie manejada es solo accesible entre llamadas a los métodos surfaceCreated y surfaceDestroyed. La implementación de la clase Callback se añade a la clase SurfaceHolder mediante el método addCallback.
En el método surfaceCreated recupero una instancia a la cámara mediante Camera.open y posteriormente se añade el acceso a la superficie subyacente mediante el método setPreviewDisplay. En el método surfaceChanged se actualizan los parámetros de la cámara y se inicia la previsualización de la imagen en la cámara. El método surfaceDestroyed cierra la cámara y libera recursos de la cámara.


package com.m607.camera;

import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.view.SurfaceHolder;

public class CameraCallback implements SurfaceHolder.Callback{
    Camera camera;
 Context ctx;
 SurfaceHolder surfaceHolder;

 public CameraCallback(Context ctx, SurfaceHolder surfaceHolder){
  this.ctx = ctx;
  this.surfaceHolder= surfaceHolder;
 }

 public void surfaceCreated(SurfaceHolder holder) {
  camera = Camera.open();
  try {
   camera.setPreviewDisplay(surfaceHolder);
  } catch (Throwable t) {
  }
 }

 public void surfaceChanged(SurfaceHolder holder, int format,
   int width, int height) {
  Parameters parameters = camera.getParameters();
  parameters.setPreviewSize(width, height);
  parameters .setPictureFormat(PixelFormat.JPEG);
  
  if (ctx.getResources().getConfiguration().orientation !=
   Configuration.ORIENTATION_LANDSCAPE) {
   // This is an undocumented although widely known feature
   parameters.set("orientation", "portrait");
   // For Android 2.2 and above
   //camera.setDisplayOrientation(90);
   // Uncomment for Android 2.0 and above
   //parameters.setRotation(90);
   } else {
   // This is an undocumented although widely known feature
   parameters.set("orientation", "landscape");
   // For Android 2.2 and above
   //camera.setDisplayOrientation(0);
   // Uncomment for Android 2.0 and above
   //parameters.setRotation(0);
   }
  

  
  camera.setParameters(parameters);
  camera.startPreview();
 }

 public void surfaceDestroyed(SurfaceHolder arg0) {
  stopCamera();
 }
 public void stopCamera(){
  camera.stopPreview();
  camera.release();
  camera = null;
 }

}








package com.m607.camera;

import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraView extends SurfaceView {

 SurfaceHolder surfaceHolder;

 CameraCallback surfaceHolderCallback;

 public CameraView(Context ctx) {
  super(ctx);

  surfaceHolder = this.getHolder();
  surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
 
  surfaceHolderCallback = new CameraCallback(ctx, surfaceHolder);
  surfaceHolder.addCallback(surfaceHolderCallback);

 }
 
 public void stopCamera(){
  surfaceHolderCallback.stopCamera();
 }
 
}


Solo falta acceder a la manejador de la cámara desde la actividad principal



 private CameraView cameraView;
        /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    try{
       super.onCreate(savedInstanceState);
       cameraView = new CameraView(
          this.getApplicationContext());
                 this.setContentView(cameraView);
    } catch(Exception e){}
    }

miércoles, 4 de abril de 2012

Primera Aplicación. Parte 5. Final

Antes que nada, en la entrada Primera Aplicación. Parte 4 se me ha olvidado indicar que para que la aplicación tenga acceso al sensor GPS del dispositivo, debe incluirse en el manifest de la aplicación la siguiente línea
...

    <uses-sdk android:minsdkversion="10">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION">
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION">
    <uses-permission android:name="android.permission.INTERNET">
...

    </uses-sdk>

Para finalizar la serie de entradas para la primera aplicación falta indicar como se lanzará la aplicación. He decidido lanzarla en background como servicio. Para ello, he creado la clase com.m607.MainService que extiende la clase android.app.Service

package com.m607;

import com.m607.database.DBManager;
import com.m607.sound.SoundManager;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.IBinder;
import android.util.Log;

public class MainService extends Service {
 
 private DBManager db;
 private SoundManager sm;
 @Override
 public void onCreate() {
  Log.v("MainService", "Service created");
  db = new DBManager(this); 
         db.open();
     
         sm = new SoundManager();
         sm.initSounds(getBaseContext());
         sm.addSound(1, R.raw.sound);
         sm.addSound(2, R.raw.beep4);
         sm.addSound(3, R.raw.beep9);
     
     
  LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
  LocationListener ll = new GpsLocationListener(this,db, sm);
  lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 50, ll);
  
     
     if(db.getRadarCount()==0){
         db.deleteAllRadars();
         db.readCSV("es-allcam.txt");
     }
 }
 
 
 @Override
 public IBinder onBind(Intent arg0) {
  // TODO Auto-generated method stub 
  return null;
 }
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startId) {
  return Service.START_STICKY;
 }
}

Esta clase instancia e inicializa el manejador de sonidos, el manejador de base de datos y el localizador GPS indicando que realice llamadas de refresco al dispositivo GPS cada segundo. En caso de que la tabla de radares de la base de datos este vacía, lee el fichero de radares y los inserta en base de datos.

Esta clase es instanciada desde el método onCreate de la actividad principal, com.m607.MainActivity, mediante una llamada al método startService de la clase Activity

<
package com.m607;

import android.app.Application;
import android.content.Intent;

public class MainActivity extends Application {
 @Override
 public final void onCreate() {
  super.onCreate();
  // se crea un servicio para localizar por GPS
  startService(new Intent(this, MainService.class));
  
 }
}
Por último, se añaden la actividad principal y el servicio en el manifest de la aplicación
...
   <application android:icon="@drawable/ic_launcher" android:label="@string/app_name">
       <service android:enabled="true" android:name=".MainService" />
       <activity android:label="@string/app_name" android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN">
                <category android:name="android.intent.category.LAUNCHER">
            </category></action></intent-filter>
        </activity>
    </application>

...

Resumen de artículos para la aplicación GPS:

Primera aplicación. Parte 1
Primera aplicación. Parte 2
Primera Aplicación. Parte 3
Primera Aplicación. Parte 4
Primera Aplicación. Parte 5


La aplicación completa se puede descargar aquí:

Primera Aplicacion. Avisador Radar para Vehículos

Magento

Estoy en estos momentos creando una tienda on-line, y para ello he seguido el consejo de un amigo y he decido usar Magento como software para la tienda. Magento es la típica aplicación LAMP (Linux, Apache, Mysql, PHP), que aunque al principio parece un follón cuando se empieza a conocer a fondo es muy interesante. Estaba acostumbrado a osCommerce, que también está muy bien para crear tiendas virtuales en Internet, pero es qué Magento, tiene hasta la posibilidad de crear aplicaciones para móvil (Android, iPhone, etc.) desde el panel de control de la tienda. Según vaya avanzando con el cacharreo de la aplicación, iré posteando mis avances.