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.

jueves, 23 de febrero de 2012

Geeksphone. Smartphone ibérico.

Escribo esta entrada para hacer un poquito de patria.

La empresa Geekphone es una empresa española que se dedica a hacer smarphones que usan sistema operativo Android y que no desmerecen a los que hay en el mercado y que está muy bien de precio.

 Yoigo me ha "regalado" un Android Samsung mini que está muy bien, pero me hubiera hecho más ilusión que me regalaran un Geekphone ZERO. En fin, por ahora por lo que veo en la web de Geekphone, solo Pepephone ofrece el dispositivo.



A ver si cunde el ejemplo en otros operadores que operan en España.

En Xataka hacen un análisis del dispositivo.

  GeeksPhone Zero, análisis (I)
  GeeksPhone Zero, análisis (II)

Actualización:

Veo que además Javier Agüera, que parece ser es el creador de esta empresa, de 19 años!!!!!, ha sido galardonado con el Premio TR35 que el Massachussets Institute Technology (MIT) concede a jóvenes emprendedores menores de 35 años. Simplemente impresionante. Aquí dejo el enlace a la noticia

 Un emprendedor de 19 años gana el TR35 del MIT


Además están a punto de sacar al mercado el Geeksphone TWO.

Primera Aplicación. Parte 4. Localización GPS

Queda para terminar la aplicación, actuar con el dispositivo GPS para obtener la localización del dispositivo móvil. Para ello, se implementa el interfaz android.location.LocationListener, que escucha cambios de localización del dispositivo. Para ello se crea la clase GpsLocationListener, que en un cambio de localización, consulta a la base de datos para saber si existen radares en las inmediaciones del dispositivo, y en caso de encontrar entradas para esas coordenadas, se avisa al conductor mediante un sonido y con un mensaje de aviso, mediante la clase android.widget.Toast
public class GpsLocationListener implements LocationListener {
Se sobreescribe el método onLocationChanged;
 public void onLocationChanged(Location location) {
  
  if (location != null) {
   Log.d("LOCATION CHANGED LATITUDE", location.getLatitude() + "");
   Log.d("LOCATION CHANGED LONGITUDE", location.getLongitude() + "");
   
   dLatitude = location.getLatitude();
   dLongitude = location.getLongitude();

   cursorRadarsByLocation = db.selectRadarsByLocation(location);
   cursorRadarsByLocation.moveToFirst();
      
            while (!cursorRadarsByLocation.isAfterLast()){          
             Location radarLocation = new Location(location);
             radarLocation.setLatitude(cursorRadarsByLocation.getDouble(0));
             radarLocation.setLongitude(cursorRadarsByLocation.getDouble(1));
             
Log.v("GpsLocationListener", "RadarDB: " + radarLocation.getLatitude() + " lon " +radarLocation.getLongitude() + " GPS: " + location.getLatitude() +":" + location.getLongitude() + " : Distance: " + radarLocation.distanceTo(location));

    if(radarLocation.distanceTo(location)<500){
Log.v("GpsLocationListener", "RADAR! la:" + location.getLatitude() + " lo:" + location.getLongitude());    
        
     View layout = inflater.inflate(R.layout.toast_layout, null);
     
     ImageView image = (ImageView) layout.findViewById(R.id.image);
     image.setImageResource(R.drawable.radar);
     TextView text = (TextView) layout.findViewById(R.id.text);
     text.setText("RADAR! la:" + location.getLatitude() + " lo:" + location.getLongitude());
     
     Toast toast = new Toast(context.getApplicationContext());
     toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
     toast.setDuration(Toast.LENGTH_LONG);
     toast.setView(layout);
     toast.show();


        sm.playSound(2);
             }
    cursorRadarsByLocation.moveToNext();
            }
  }
 }

En este método, se almacenan las coordenadas de la nueva localización y se realiza una consulta a la base de datos mediante una llamada al método selectRadarsByLocation. En caso de encontrar radares en las inmediaciones, se verifica que el radar se encuentre en las inmendiaciones mediante el método distanceTo de la clase Location, y en caso de cumplir la condición, se avisa al usuario mediante una imagen y un sonido. Para crear un componente que Toast con una imagen y un texto, se crea el layout toast_layout.xml y se almacena en "res/layout"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/toast_layout_root"
              android:orientation="horizontal"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:padding="10dp"
              android:background="#DAAA"
              >
    <ImageView android:id="@+id/image"
               android:layout_width="wrap_content"
               android:layout_height="fill_parent"
               android:layout_marginRight="10dp"
               />
    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="fill_parent"
              android:textColor="#FFF"
              />
</LinearLayout>

Este layout esta compuesto por una imagen ImageView identificado por "@+id/image" y un texto TextView identificado por "@+id/text". Posteriormente, en el método onLocationChanged se recuperan estas vistas mediante sus identificadores R.id.image y R.id.text. Este layout se asigna a la vista del objeto toast y se muestra llamando a su método show.
View layout = inflater.inflate(R.layout.toast_layout, null);
     
     ImageView image = (ImageView) layout.findViewById(R.id.image);
     image.setImageResource(R.drawable.radar);
     TextView text = (TextView) layout.findViewById(R.id.text);
     text.setText("RADAR! la:" + location.getLatitude() + " lo:" + location.getLongitude());
     
     Toast toast = new Toast(context.getApplicationContext());
     toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
     toast.setDuration(Toast.LENGTH_LONG);
     toast.setView(layout);
     toast.show();
Posteriormente se llama al manager de sonido mediante una clase SoundManager sm, que se ha implementado previamente
package com.m607.sound;

import java.util.HashMap;

import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
import android.util.Log;



public class SoundManager {
 
 private  SoundPool spSoundPool; 
 private  HashMap<Integer, Integer> hmSoundPoolMap; 
 private  AudioManager  amAudioManager;
 private  Context context;
 
 
 public SoundManager()
 {
  
 }
  
 public void initSounds(Context theContext) { 
   context = theContext;
      spSoundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0); 
      hmSoundPoolMap = new HashMap<Integer, Integer>(); 
      amAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);       
 } 
 
 public void addSound(int Index,int SoundID)
 {
  hmSoundPoolMap.put(Index, spSoundPool.load(context, SoundID, 1));
 }
 
 public void playSound(int index) { 
      int streamVolume = amAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 
      spSoundPool.play(hmSoundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f); 
 }
 
 public void playLoopedSound(int index) { 
  
      int streamVolume = amAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 
      spSoundPool.play(hmSoundPoolMap.get(index), streamVolume, streamVolume, 1, -1, 1f); 
 }
}
Esta clase se instancia en la clase en el Service principal, de la siguiente manera
     sm = new SoundManager();
     sm.initSounds(getBaseContext());
     sm.addSound(1, R.raw.sound);
     sm.addSound(2, R.raw.beep4);
     sm.addSound(3, R.raw.beep9);
     
Los sonidos, como las imágenes, se han almacenado en res/raw y se recuperan como se muestra en el trozo de código anterior.

martes, 24 de enero de 2012

Primera Aplicación. Parte 3

Cargando los datos en base de datos.

Para cargar los datos del fichero CSV en base de datos, es necesario añadir este fichero en el proyecto de Eclipse. Para ello es necesario importar este archivo en la carpeta assets del proyecto.

Para ello, hay que seleccionar "File>Import..." en el proyecto actual.

Posteriormente hay que seleccionar "File System":
Selecciono el directorio donde esta almacenado el fichero es-allcam.txt y se importa este fichero a la carpeta assets del proyecto. Una vez incluido este fichero en el proyecto, accedo a él para parsearlo e insertar la información en base de datos. El siguiente código accede al fichero es-allcam.txt, abre un flujo de lectura y parsea los datos fila por fila del fichero insertando los datos como una fila en la tabla de base de datos mediante el método insertRadar
 
 public boolean readCSV(String radarCSVFile) {
  try {
   InputStream is = context.getAssets().open("es-allcam.txt");
   InputStreamReader isr = new InputStreamReader(is);
   BufferedReader in = new BufferedReader(isr);
   in.readLine();
   String reader = "";
   String[] RowData;
   db.beginTransaction();
   while ((reader = in.readLine()) != null) {
    RowData = reader.split(",");
    Log.w(TAG, "Inserting... " + RowData[1] + " " + RowData[0]
      + " " + RowData[2] + " " + RowData[3] + " "
      + RowData[4] + " " + RowData[5] + " " + "...");
    
    insertRadar(RowData[1], RowData[0], RowData[2], RowData[3],
      RowData[4], RowData[5], "");

   }
   db.setTransactionSuccessful();

   db.releaseMemory();
   in.close();
  } catch (Exception e) {
   db.releaseMemory();
   e.printStackTrace();
  } finally {
   db.endTransaction();
  }
  return true;
 }

El método insertRadar luce como se muestra a continuación
 public void insertRadar(String latitude, String longitude,
   String radar_type, String speed, String direction_type,
   String direction, String postal_code) {
  rowSet[0] = latitude;
  rowSet[1] = longitude;
  rowSet[2] = radar_type;
  rowSet[3] = speed;
  rowSet[4] = direction_type;
  rowSet[5] = direction;
  rowSet[6] = postal_code;

  // return db.insert(DATABASE_TABLE, null, initialValues);
  db.execSQL(sqlInsertRadar, rowSet);

 
 }

Implemento unos cuantos métodos para tratar los datos de radar en tablas. El siguiente método actualiza los radars por id de radar en base de datos,

 // ---updates a Radar---
 public boolean updateRadar(long rowId, String latitude, String longitude,
   String radar_type, String speed, String direction_type,
   String direction, String postal_code) {
  Log.v(TAG, "update radar " + rowId);
  ContentValues args = new ContentValues();
  args.put(KEY_LATITUDE, latitude);
  args.put(KEY_LONGITUDE, longitude);
  args.put(KEY_RADARTYPE, radar_type);
  args.put(KEY_SPEED, speed);
  args.put(KEY_DIRECTION_TYPE, direction_type);
  args.put(KEY_DIRECTION, direction);
  args.put(KEY_POSTALCODE, postal_code);
  return db.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
 }

Este método sirve para recuperar los radares que se encuentren cerca de cierta localización dada por un objeto Location. Este método se utilizará posteriormente para buscar radares en el momento de que se actualice la localización del móvil mediante su localizador GPS.

 public Cursor getRadarsByLocation(Location location) {

  String sConditional = KEY_LATITUDE + " BETWEEN "
    + String.valueOf(location.getLatitude() + 0.001) + " AND "
    + String.valueOf(location.getLatitude() - 0.001) + " AND "
    + KEY_LONGITUDE + " BETWEEN "
    + String.valueOf(location.getLongitude() + 0.001) + " AND "
    + String.valueOf(location.getLongitude() - 0.001);

  Log.v(TAG, sConditional);

  if (location != null) {

   Log.v(TAG, "get radar by location: " + location.getLatitude() + " "
     + location.getLongitude());

   mCursor = db.query(true, DATABASE_TABLE, new String[] { KEY_ROWID,
     KEY_LATITUDE, KEY_LONGITUDE, KEY_RADARTYPE, KEY_SPEED,
     KEY_DIRECTION_TYPE, KEY_DIRECTION, KEY_POSTALCODE },
     sConditional, null, null, null, null, null);
  }
  if (mCursor != null) {
   mCursor.moveToFirst();
  }
  return mCursor;

 }

domingo, 22 de enero de 2012

Primera aplicación. Parte 2

Obvio lo obvio, como bajarse el IDE Eclipse, instalar Android SDK y el ADT de eclipse.
Para una guia muy bien explicada

http://developer.android.com/sdk/index.html

Para los que además tienen un equipo con arquitectura de 64 bits.

http://developer.android.com/sdk/installing.html#troubleshooting

Comienzo con la base de datos.
Los datos de localización de los radares móviles los he sacado de la página SpeedCameraPOI.com de la que se puede bajar un fichero txt en formato csv con los datos de localización de los radares.


1:  lon,lat,TYPE,SPEED,DIRTYPE,DIRECTION
2:  -17.851917,28.653617,5,0,0,0
3:  -16.825796,28.198766,5,0,0,0
4:  -16.817533,28.265373,5,0,0,0
5:  -16.797592,28.168118,5,0,0,0
6:  -16.797476,28.241514,5,0,0,0
7:  -16.791524,28.147537,5,0,0,0
8:  -16.765271,28.183670,5,0,0,0
9:  -16.653410,28.394205,5,0,0,0
10:  -16.580407,28.391820,5,0,0,0
11:  -16.575469,28.061445,5,0,0,0
12:  -16.373555,28.355344,5,0,0,0
13:  -16.349598,28.489710,5,0,0,0
14:  -16.273584,28.443848,5,0,0,0
15:  ....
Una vez que tengo los datos de localización de los radares, y viendo la estructura que poseen, decido que la base de datos va a consistir de una sola tabla RADARLOCATION con seis columnas; LONGITUDE, LATITUDE, RADARTYPE, SPEED, DIRECTIONTYPE

De acuerdo. Ahora viene lo divertido, ¿Como accedo a una base de datos sqlite?

Para ello Android ofrece la clase abstracta android.database.sqlite.SQLiteOpenHelper con los métodos onCreate y onUpgrade. El método onCreate se llama la primera vez que se crea la base de datos, en mi caso, RADAR_LOCATION y con el método onUpgrade, se actualiza la base de datos, en caso de que modifiquemos la versión de esta.

 private static class DatabaseHelper extends SQLiteOpenHelper {
  DatabaseHelper(Context context) {
   super(context, DATABASE_NAME, null, DATABASE_VERSION);
  }

  @Override
  public void onCreate(SQLiteDatabase db) {
   Log.w(TAG, "Create database " + DATABASE_NAME);
   db.execSQL(DATABASE_CREATE);
  }

  @Override
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
   Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
     + newVersion + ", which will destroy all old data");
   db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE);
   onCreate(db);
  }
 }
La creación de la base de datos se realiza en el constructor de la clase DatabaseHelper que hereda SQLiteOpenHelper, llamando al constructor de la clase heredada, pasando como argumento el nombre de la base de datos, en este caso radars y la versión de la base de datos. DATABASE_CREATE es un String con la sentencia SQL para crear la base de datos:
 private static final String DATABASE_CREATE = "create table "
   + DATABASE_TABLE
   + " (_id integer primary key autoincrement, "
   + "latitude real not null, longitude real not null, radar_type integer not null, speed integer not null, direction_type integer not null, direction integer not null, postal_code text)";
En caso de que la versión de la base de datos se modifique, se ejecuta el método onUpgrade, que hace un drop de la tabla radar_location y vuelve a crearla. Queda por último crear la clase que hará uso de esta clase Helper, Para la siguiente entrada...