viernes, 23 de noviembre de 2012

Android: Chequear conectividad con WiFi

Para poder chequear la conectividad con WiFi primero hay que agregar los permisos respectivos en el manifiesto de Android:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.mytoolbox"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="9"
        android:targetSdkVersion="15" />

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
 ...
 
</manifest>

Aquí dejo un código que verifica la conectividad. Si no existe entonces despliega un mensaje indicando al usuario que debe haber conectividad para poder usar la aplicación. Seguidamente termina la aplicación.

private void checkWiFiConnectivity() {
  ConnectivityManager connMan =  (ConnectivityManager) 
       getSystemService(Context.CONNECTIVITY_SERVICE);
  boolean wifiIsOn = connMan.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
        .isConnectedOrConnecting();
  Log.d("GABO", "WiFi is : " + wifiIsOn);
  
  if(!wifiIsOn) {
   finishApplication();
  }  
}

private void finishApplication() {
  AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
  builder.setMessage("You need WiFi connection to access this App!");
  builder.setCancelable(false);
     
  builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
   
   @Override
   public void onClick(DialogInterface dialog, int which) {
       MainActivity.this.finish();    
   }
  });
     
  AlertDialog alert = builder.create();
  alert.show();  
}

miércoles, 21 de noviembre de 2012

MongoDB bajo contexto


Un par de semanas atrás asistí como es de costumbre cuando se da la oportunidad, a una de las charlas del Grupo de Usuarios de Java de Costa Rica (CRJUG). En esta ocasión organizado por los amigos de BodyBuilding.com. En esta ocasión nos dieron una charla introductiva al mundo de MongoDB, un motor no-sql de base de datos que usan en su sitio web. Disfruté mucho el estilo relajado de las presentaciones. Algo así como “Por programadores para programadores”. Nos mostraron las principales diferencias entre este nuevo paradigma en comparación con el tradicional paradigma relacional. Creo que desde un principio fueron bastante transparentes afirmando que no habían venido en una “misión evangelística” de la tecnología. Ellos saben al igual que muchos que el uso de este nuevo paradigma es para determinados propósitos. No es para substituir las bases de datos relacionales con sus características típicas: seguridad, transaccionabilidad, integridad referencial, etc. Como en muchos otros ámbitos en ciencias de la computación donde siempre existe una tensión constante en tratar de balancear todos los elementos claves de un sistema: seguridad, robusticidad, velocidad, confiabilidad, etc,  las bases de datos no escapan de esta batalla y por tanto han comenzado a aparecer nuevos métodos para inclinar la balanza sobre un grupo de características deseables en ciertos contextos, como lo es la escalabilidad.

Estoy enfatizando este punto por algo interesante que observé durante la presentación. Debido al sistema informal de la presentación, siempre hubo apertura para hacer preguntas y comentarios en todo momento y entoces algunos participantes en varias ocasiones insistieron en comparar esta solución con la típica solución de base de datos relacional. Esto en principio está bien para señalar el por qué de las diferencias, pero no obstante lo que encontré interesante fue lo que percibí la manera en que se hacían los aportes. Como con cierta duda y escepticismo, como “no puedo creer que las reglas de toda una vida se estén violando”. Por supuesto que nadie dijo esto de manera literal, pero solo describo mi percepción de la forma defensiva en que se hacía. Por ejemplo un participante comentó que Oracle atacaba un problema de una manera distinta sin abandonar el paradigma relacional. En otra ocasión hubo una breve argumentación de por qué MongoDB no continuó con un lenguaje de consulta similar el tipo SQL, porque después de todo, toda la comunidad de desarrolladores ya está acostumbrada a usar este lenguaje.

Esta reacción de una parte del público es interesante, pero no del todo sorprendente. Las bases de datos relacionales han estado con nosotros por más de 40 años. Son más viejas que muchos de nosotros. Es por ellos que a muchos les cuesta creer que sea necesario reinventar el agua tibia. Es similar a que apareciera un nuevo movimiento defendiendo las virtudes de un lenguaje de programación que abandona la programación orientada a objetos. Sería completamente normal para muchos mirar esto con ojos totalmente escépticos. En mi humilde opinión considero que la experimentación en nuevos paradigmas es completamente necesario para desarrollar innovación en cualquier ámbito científico y tecnológico. Y también creo por otro lado que no debemos conducirnos de una manera “hipsteriana”, intentando probar y hasta siendo promotores de las últimas tecnologías solo por lucir “cool”, llegando al punto inclusive de etiquetar a otros como anticuados por no usar lo más reciente en frameworks, librerías, metodologías, etc. Las bases de datos relacionales están a décadas creo yo de entrar en una fase de retiro, pero igual debemos comenzar a considerar las nuevas opciones que ya están comenzando aparecer en el escenario desde hace algunos años atrás.

En este video bastante gracioso (la verdad cada vez que lo veo me causa risa de nuevo), se señala de manera cómica este último punto que menciono, donde tenemos personas bastante creídas y apresuradas para descartar soluciones sólidas y probadas solo porque hay algo nuevo de moda. Jugando como dije al hipster pero con tecnologías de desarrollo. No creo que este video sea una crítica en específico a MongoDB, sino más bien una crítica a la gente que no entiende el contexto apropiado de ciertas tecnologías y creen que es una zapato que le queda bien a todo mundo. Si uno tiene una aplicación sencilla sin grandes problemas de concurrencia por el momento, uno no debería estar preocupándose muy al principio por escalabilidad. Sí dejar un diseño no acoplado solamente a una base de datos, pero no entrar en pánicos innecesarios. Sobre arquitecturar una aplicación puede causar más problemas de los que puede solucionar. Pero ese es otro tema que comentaré después a su debido tiempo.


viernes, 16 de noviembre de 2012

Evitar modo IE7 Standards

Estabamos trabajando un sitio, donde por razones fuera de nuestro control, no teníamos suficiente tiempo para pulir el sitio para soportar Internet Explorer 7 (que es como el nuevo IE6 de nuestros tiempos). El sitio funcionaba bien en los navegadores serios como Chrome, FF y IE8, pero por alguna razón siempre el navegador, a pesar de ser versión 8, cargaba el modo de IE7 Standards.


Un poquito de "googlismo" y encontré que hay un meta tag para forzar al navegador usar la versión más alta:

<meta http-equiv="X-UA-Compatible" content="IE=edge">

lunes, 5 de noviembre de 2012

CakePHP: Salvar y Mostrar

En este pequeño código muestro como guardar los datos de un formulario, en este caso correspondientes a un anuncio, e inmediatamente redirigir a la página para ver los datos guardados.

<?php

//...

class AnunciosController extends AppController {
  
 public function add() {  
        if ($this->request->is('post')) {
         if ($this->Anuncio->save($this->request->data)) {
              $this->Session->setFlash('El anuncio fue guardado: ');
              $inserted_id = $this->Anuncio->id;              
              $this->redirect(array('action' => 'view', $inserted_id ));
         } else {
             $this->Session->setFlash('No se pudo guardar el anuncio');
         }
  }
  // ...
    }
    
 // ...
 
 public function view($id) {
        $this->Anuncio->id = $id;
        $this->set('anuncio', $this->Anuncio->read());
    }    
}

La línea clave es la que hace el redireccionamiento. Aquí tocó quebrarme bastante la cabeza para determinar como pasaba el id del recién insertado anuncio.

Android: "Piedra, papel y tijeras"

Aquí dando mis primeros gateos en el mundo de desarrollo de Android. En esta simple aplicación ejemplifico el uso de:
  • Buttons
  • Click events
  • Image Views
  • Alert dialogs
La aplicación es un simple juego de"Piedra, Papel y Tijeras". El usuario hace la selección entre las clásicas tres opciones, y luego Android hace una selección aleatoria. De acuerdo a las básicas reglas universales de este conocido juego, el resultado es determinado y mostrado en un mensaje de alerta.

Este es el layout básico del juego. Las dos imágenes de signo de interrogación son para desplegar las dos selecciones. Las otras tres imágenes son para que el usuario haga la selección.



















XML del layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="20dp"
        android:padding="@dimen/padding_medium"
        android:text="Rock, Paper, Scissors!"
        tools:context=".RockPaperScissors" />

    <Button
        android:id="@+id/buttonRock"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="147dp"
        android:layout_marginRight="20dp"
        android:layout_toLeftOf="@+id/textView1"
        android:background="@drawable/rock_small" />

    <Button
        android:id="@+id/buttonPaper"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/buttonRock"
        android:layout_alignBottom="@+id/buttonRock"
        android:layout_centerHorizontal="true"
        android:background="@drawable/paper_small" />

    <Button
        android:id="@+id/buttonScissors"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/buttonPaper"
        android:layout_alignBottom="@+id/buttonPaper"
        android:layout_marginLeft="23dp"
        android:layout_toRightOf="@+id/textView1"
        android:background="@drawable/scissors_small" />

    <ImageView
        android:id="@+id/imageViewAnswerUser"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView1"
        android:layout_marginTop="26dp"
        android:layout_toLeftOf="@+id/buttonPaper"
        android:src="@drawable/question" />

    <ImageView
        android:id="@+id/ImageViewAnswerAndroid"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@+id/buttonScissors"
        android:layout_alignTop="@+id/imageViewAnswerUser"
        android:src="@drawable/question" />

    <ImageButton
        android:id="@+id/imageButtonHome"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@+id/buttonPaper"
        android:layout_below="@+id/buttonPaper"
        android:layout_marginTop="38dp"
        android:src="@drawable/home" />

</RelativeLayout>

código de la actividad:
package com.example.mytoolbox;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;

public class RockPaperScissors extends Activity implements OnClickListener {
 
 public enum Option {
  ROCK, PAPER, SCISSORS
 }
 
 public enum Result {
  WIN, LOSS, DRAW  
 }
 
 private Option userSelection;
 private Result gameResult;
 

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rock_paper_scissors);
        
        Button buttonRock = (Button) findViewById(R.id.buttonRock);
        Button buttonpaper = (Button) findViewById(R.id.buttonPaper);
        Button buttonScissors = (Button) findViewById(R.id.buttonScissors);
        ImageButton buttonHome = (ImageButton) findViewById(R.id.imageButtonHome);
        
        // Set click listening event for all buttons.
        buttonRock.setOnClickListener(this);
        buttonpaper.setOnClickListener(this);
        buttonScissors.setOnClickListener(this);
        buttonHome.setOnClickListener(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_rock_paper_scissors, menu);
        return true;
    }

 @Override
 public void onClick(View v) {
  
  ImageView imageView = (ImageView) findViewById(R.id.imageViewAnswerUser);
  boolean play = true;
  
  switch (v.getId()) {
   case R.id.buttonRock:
    userSelection = Option.ROCK;
    imageView.setImageResource(R.drawable.rock);
    break; 
   case R.id.buttonPaper:
    userSelection = Option.PAPER;
    imageView.setImageResource(R.drawable.paper);
    break;
   case R.id.buttonScissors:
    userSelection = Option.SCISSORS;
    imageView.setImageResource(R.drawable.scissors);
    break;
   case R.id.imageButtonHome:
    startActivity(new Intent(RockPaperScissors.this, ChooseActivity.class)); // To go home.
    play = false;
    break;
  }
  
  if(play) {
   play();
   showResults();
  }
 }

 private void showResults() {
  AlertDialog.Builder builder = new AlertDialog.Builder(RockPaperScissors.this);     
     builder.setCancelable(false);     
     builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {   
   @Override
   public void onClick(DialogInterface dialog, int which) {
    // Do nothing   
   }
  });
     
     // Sets the right message according to result.
     if(gameResult == Result.LOSS) {
      builder.setMessage("You Loose!");
     } else if(gameResult == Result.WIN) {
      builder.setMessage("You Win!");
     } else if(gameResult == Result.DRAW) {
      builder.setMessage("It's a draw!");
     } 
     
     AlertDialog alert = builder.create();
     alert.show();  
    } 

 private void play() {
  // Generates a random play.
  int rand =  ((int)(Math.random() * 10)) % 3;
  Option androidSelection = null;
  ImageView imageView = (ImageView) findViewById(R.id.ImageViewAnswerAndroid);
  
  // Sets the right image according to random selection.
  switch (rand) {
   case 0:
    androidSelection = Option.ROCK;
    imageView.setImageResource(R.drawable.rock);
    break;
   case 1:
    androidSelection = Option.PAPER;
    imageView.setImageResource(R.drawable.paper);
    break;
   case 2:
    androidSelection = Option.SCISSORS;
    imageView.setImageResource(R.drawable.scissors);
    break;
  }
  // Determine game result according to user selection and Android selection.
  if(androidSelection == userSelection) {
   gameResult = Result.DRAW;
  }
  else if(androidSelection == Option.ROCK && userSelection == Option.SCISSORS) {
   gameResult = Result.LOSS;
  }
  else if(androidSelection == Option.PAPER && userSelection == Option.ROCK) {
   gameResult = Result.LOSS;
  } 
  else if(androidSelection == Option.SCISSORS && userSelection == Option.PAPER) {
   gameResult = Result.LOSS;
  } else {
   gameResult = Result.WIN;
  }  
 }    
}

App corriendo: