jueves, 24 de mayo de 2012

Implementación de ventana de acceso con Login. Android Tutorial


El objetivo de este tutorial es implementar de forma sencilla un mecanismo de protección para nuestra aplicación, creando una ventana de “Login” con un nombre de usuario y una contraseña. Esta dialogo de Login también podrá ser usado o para proteger cualquier opción de menú de la aplicación
  • Desarrollaremos el tutorial para un TABLET con la versión 4.0 del SDK de Android (Ice Cream Sandwich). Válido también para Android 2.x y 3.x.
  • Veremos cómo trabajar con las opciones de MENU de la barra de tareas. Y también con las PREFERENCES.
  • Pondremos por defecto unas credenciales para acceder a las “Preferences”  (usuario, contraseña),  para comenzar a trabajar y después las podremos cambiarlas.
  • Mostraremos el contenido de las PREFERNCES, en el lugar de su descripción, cosa que no hace Android por defecto.
  • Veremos como añadir por código objetos (Layouts, TextView y EditText) a un AlertDialog, usando la propiedad Buider del AlertDialog.

1.- Lo primero es crear un proyecto nuevo, ya sea con Eclipse o IntelliJ IDEA, utilizaremos la versión 4 del SDK de Android (Ice CreamSandwich).

Para este tutorial vamos a utilizar IntelliJ IDEA 11.1. Después de crear el proyecto tendríamos algo parecido a esto:


2.- Creación de un Menú para acceder a las Preferences de Android.

Comenzaremos con la creación de un menú para poder acceder a las PREFERENCES, para ello crearemos una nueva carpeta en nuestro proyecto dentro “res” llamada menu y dentro creamos el archivo menu.xml con el siguiente contenido:

    

Insertar en el archivo de recursos “res\values\strings.xml” las siguientes líneas con el valor de las etiquetas del menú:
    Settings...
    Login Settings 
Nos quedaría el proyecto de la siguiente forma con nuestro menu.xml:

Después nos vamos a la clase asociada a nuestra actividad principal (MyActivity) “src\com.movalink.LoginTutorial\MainActivity” para incluir el siguiente procedimiento que hace un Override del existente.
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        MenuInflater menuInflater = getMenuInflater();
        menuInflater.inflate(R.menu.menu, menu);
        return true;
    }



Lanzamos un Emulador creado con la versión 4.0 de Android (Ice Cream Sandwich).

Ahora ejecutamos la aplicación para que corra en el Emulador y vemos que en la esquina superior derecha aparece un botón con tres puntos verticales, es la opción de menú por defecto que nos muestra Android. Si pulsamos en ella vemos la opción de menú que hemos creado.


3.- Trabajando con las Preferences.

El siguiente paso, es crear dos campos en nuestras “Preferences” para poner el ellos el nombre de usuario y la contraseña.

Lo primero que debemos hacer es crear una carpeta en “res” con el nombre “xml” y dentro de ella crearemos un archivo con formato XML llamado “settings.xml” al cual añadiremos el siguiente contenido:

    

    


Las cadenas de caracteres usadas en este archivo ej. "@string/username_settings_title",  las declaramos con su valor en “res\values\strings.xml”:
Nombre de usuario
    Teclee el nombre de usuario
    Nombre de usuario

    Contraseña
    Teclee la contraseña
    Contraseña

A continuación crearemos la clase para manipular las “Preferences”, lo podemos hacer con clic derecho en com.movalink.LoginTutorial , en el menú contextual vamos a “New” y pulsamos “Android Component” 












En la carpeta ““src\com.movalink.LoginTutorial\” creamos una Activity y la llamamos Preferences, que a su vez va a heredar de PreferenceActivity (Cambiamos Activity por PreferenceActivity) e implementar el método SharedPreferences.OnSharedPreferenceChangeListener.

Nuestra clase “Preferences.java” quedaría de la siguiente manera:
package com.movalink.LoginTutorial;
import android.content.SharedPreferences;
import android.preference.PreferenceActivity;
public class Preferences extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
//En el método onCreate de la Activity hacemos referencia a nuestro archivo de settings.xml 
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.settings);
    
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
        //To change body of implemented methods use File | Settings | File Templates.
    }
}
A continuación crearemos dos constantes estáticas a nuestra clase
private static final String USERNAME_SETTINGS ="USERNAME_SETTINGS";
private static final String USERNAME_SETTINGS_DEFAULT = "admin";
private static final String PASSWORD_SETTINGS = "PASSWORD_SETTINGS";
private static final String PASSWORD_SETTINGS_DEFAULT ="1234";
Nota: Tener en cuenta  que el nombre de estas constantes es el mismo que  “android:key="USERNAME_SETTINGS"” en el archivo “settings.xml
Como se puede observar los valores por defecto son
Username: admin
Password: 1234
Ahora crearemos dos métodos públicos para poder leer el nombre de usuario y la contraseña que pondremos en las “Preferences”.
    public static String getUsernameSettingsValue(Context context) {
        return PreferenceManager.getDefaultSharedPreferences(context).
                getString(USERNAME_SETTINGS, USERNAME_SETTINGS_DEFAULT);
    }

    public static String getPasswordSettingsValue(Context context) {
        return PreferenceManager.getDefaultSharedPreferences(context).
                getString(PASSWORD_SETTINGS, PASSWORD_SETTINGS_DEFAULT);
    }
Ahora regresamos a nuestra actividad principal MainActivity, para sobrescribir el método que nos permite capturar el evento de selección de las opciones de Menu, en nuestro caso solo hemos creado la opción de Settings para acceder a las “Preferences”. Como podemos ver al seleccionar Settings lo que hacemos es llamar a la Acividad Prferences a través de su clase.
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.settings:
                startActivity(new Intent(getApplicationContext(), Preferences.class));
                return true;
        }
        return false;
    }
Hasta el momento nuestro Actividad principal (MyActivity), quedaría de la siguiente manera:
package com.movalink.LoginTutorial;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

public class MyActivity extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        MenuInflater menuInflater = getMenuInflater();
        menuInflater.inflate(R.menu.menu, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.settings:
                startActivity(new Intent(getApplicationContext(), Preferences.class));
                return true;
        }
        return false;
    }
}

4.- Mostrando el contenido de las Preferences.
Ahora ejecutamos la aplicación para que corra en el Emulador,  en la esquina superior derecha pulsamos el botón de menú por defecto que nos muestra Android con nuestros “Settings”, y vemos una nueva ventana con los dos parámetros que hemos creados, Nombre de Usuario y Contraseña.








Si pulsamos, por ejemplo Nombre de Usuario, nos aparece un dialogo para introducir la información correspondiente.
Tecleamos Nombre de Usuario: movalink, y pulsamos OK, como podemos observar no se muestra la información, de momento solo es posible si volvemos a pulsar en las preferences el Nombre de Usuario.
Con la contraseña hacemos el mismo proceso y tecleamos Contraseña: 1234
Como mostrar el contenido de nuestras Preferences.
Para mostrar en el lugar de la descripción el contenido de las Preferecias, crearemos un par de métodos que nos ayudaran.
En nuestra clase “Preferences” adicionamos los siguientes métodos privados
/**
     * Set the summaries of all preferences     
*/
private void initSummaries(PreferenceGroup pg) {
    for (int i = 0; i < pg.getPreferenceCount(); ++i) {
        Preference p = pg.getPreference(i);
        if (p instanceof PreferenceGroup)
            this.initSummaries((PreferenceGroup) p); // recursion
        else
           this.setSummary(p);
    }
}
/**
* Set the summaries of the given preference
*/
private void setSummary(Preference pref) {
// react on type or key
    if (pref instanceof ListPreference) {
        ListPreference listPref = (ListPreference) pref;
        pref.setSummary(listPref.getEntry());
    }
    if (pref instanceof EditTextPreference) {
        EditTextPreference editPref = (EditTextPreference) pref;
        pref.setSummary(editPref.getText());
    }
}
Ahora en el método onCreate de la clase hacemos el llamado a estas dos funciones, para asi mostrar en la descripción, el contenido de las Preferences.
this.initSummaries(this.getPreferenceScreen());
this.getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
Ejecutamos la aplicación y vamos al menú de nuestras “Preferences” y podemos ver lo siguiente:







Así quedaría nuestra clase Preferences.java
package com.movalink.LoginTutorial;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.*;

public class Preferences extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
    private static final String USERNAME_SETTINGS = "USERNAME_SETTINGS";
    private static final String USERNAME_SETTINGS_DEFAULT = "admin";    
    private static final String PASSWORD_SETTINGS = "PASSWORD_SETTINGS";
    private static final String PASSWORD_SETTINGS_DEFAULT = "1234";

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.settings);

        this.initSummaries(this.getPreferenceScreen());

        this.getPreferenceScreen().getSharedPreferences()
                .registerOnSharedPreferenceChangeListener(this);
    }
    /**
     * Set the summaries of all preferences
     */
    private void initSummaries(PreferenceGroup pg) {
        for (int i = 0; i < pg.getPreferenceCount(); ++i) {
            Preference p = pg.getPreference(i);
            if (p instanceof PreferenceGroup)
                this.initSummaries((PreferenceGroup) p); // recursion
            else
                this.setSummary(p);
        }
    }
    /**
     * Set the summaries of the given preference
     */
    private void setSummary(Preference pref) {
        // react on type or key
        if (pref instanceof ListPreference) {
            ListPreference listPref = (ListPreference) pref;
            pref.setSummary(listPref.getEntry());
        }
        if (pref instanceof EditTextPreference) {
            EditTextPreference editPref = (EditTextPreference) pref;
            pref.setSummary(editPref.getText());
        }
    }
    public static String getUsernameSettingsValue(Context context) {
        return PreferenceManager.getDefaultSharedPreferences(context).
                getString(USERNAME_SETTINGS, USERNAME_SETTINGS_DEFAULT);
    }
    public static String getPasswordSettingsValue(Context context) {
        return PreferenceManager.getDefaultSharedPreferences(context).
                getString(PASSWORD_SETTINGS, PASSWORD_SETTINGS_DEFAULT);
    }
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
        //To change body of implemented methods use File | Settings | File Templates.
    }
}

5.- Iniciar la Aplicación con el diálogo de Usuario y Contraseña.

Una vez creadas nuestras credenciales en las “Preferences”, es el momento de comenzar a  usarlas.
La idea es que cuando arranque nuestra aplicación nos muestre un dialogo con Usuario y Contraseña, para poder acceder a la misma.
Lo primero que haremos es crear las siguientes constantes y variables privadas en nuestra actividad principal (MyActivity):

Estas constantes son para validar de acceso y almacenar el contenido de nuestras Preferences.
private static final int LOGIN_ACTION = 0;
private static final int SETTINGS_ACTION = 1;
private boolean validUser = false;
private String userNamePreferences;
private String passwordPreferences;

A continuación hacemos un override del método onStart(), y es aquí donde obtendremos los valores guardados en las “Preferences”. Llamaremos a la función que nos muestra el Login, en este caso pasaremos la constante LOGIN_ACTION, ya que solo vamos a validar el usuario y la contraseña para acceder a la aplicación.
@Override
protected void onStart(){
    super.onStart();
    if (!validUser){
        userNamePreferences = Preferences.getUsernameSettingsValue(this);
        passwordPreferences = Preferences.getPasswordSettingsValue(this);
        //Esta es la función que nos muestra el diálogo de Login 
        showAccessPopUpDialog(userNamePreferences, passwordPreferences, LOGIN_ACTION);
    }
}

Este procedimiento showAccessPopUpDialog(…) nos permite proteger otras opciones de menú de la aplicación. El procedimiento lo colocamos en muestra actividad principal (MyActivity). 
Al final del tutorial se incluye el contenido de este procedimiento con todo los comentarios explicativos.

6.- Protegiendo las Preferences con el diálogo de Usuario y Contraseña

En el siguiente ejemplo vamos a proteger con usuario y contraseña el acceso a las “Preferences

Anteriormente se accedía directamente a las “Preferences” pulsando en Settings…, como podemos ver hemos comentado el llamado directo y ahora utilizamos showAccessPopUpDialog(userNamePreferences, passwordPreferences, SETTINGS_ACTION);    
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
       case R.id.settings:
       //startActivity(new Intent(getApplicationContext(), Preferences.class));
       //Show popup dialog to permit access to preferences
           showAccessPopUpDialog(userNamePreferences, passwordPreferences, SETTINGS_ACTION);
           return true;
        }
        return false;
    } 

A continuación mostramos el contenido de los procedimientos que hacen posible el funcionamiento de nuestra LOGIN, ambos procedimientos debemos agregarlos a nuestra actividad principal MyActivity. showAccessPopUpDialog(…) y loginAction(...)

Así quedaría nuestro dialogo de Login Access en Android



Procedimiento: showAccessPopUpDialog(…)

showAccessPopUpDialog(final String userName, final String password, final int action) y loginAction(int action)
    private void showAccessPopUpDialog(final String userName, final String password, final int action) {
        final AlertDialog.Builder helpBuilder = new AlertDialog.Builder(this);
        helpBuilder.setTitle("Acceso a clientes");
        helpBuilder.setMessage("Introduzca usario y contraseña");
        helpBuilder.setIcon(R.drawable.key_stroke_32x32);

        //Creamos un TextView para la etiqueta Usuario
        final TextView LabelName = new TextView(this);
        LabelName.setWidth(100);
        LabelName.setText("Usuario:");

        //Creamos un EditText para teclear el valor del campo Usuario
        final EditText inputName = new EditText(this);
        inputName.setSingleLine();
        inputName.setWidth(150);
        inputName.setInputType(InputType.TYPE_CLASS_TEXT);
        inputName.setText("");

        //Creamos un TextView para la etiqueta Contraseña
        final TextView LabelPass = new TextView(this);
        LabelPass.setWidth(100);
        LabelPass.setText("Contraseña:");

        //Creamos un EditText para teclear el valor del campo Contraseña
        final EditText inputPass = new EditText(this);
        inputPass.setSingleLine();
        inputPass.setWidth(150);
        inputPass.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
        inputPass.setText("");

        //Definimos parametros para luego aplicar al Layout
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT);

        //Creamos un Layout comun para agrupar la entiqueta(TexView) y el campo (EditText)
        //Tanto del Usuario como de la Contraseña
        LinearLayout layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);
        layout.setGravity(Gravity.CENTER_HORIZONTAL);
        layout.setPadding(10,5,0,5);

        //Creamos un Layout para agrupar la entiqueta(TexView) y el campo (EditText)
        // del Usuario
        LinearLayout layoutUser = new LinearLayout(this);
        // Aplicamos los parámetros del layout a nuestros objectos
        layoutUser.setLayoutParams(params);
        layoutUser.addView(LabelName);
        layoutUser.addView(inputName);

        //Creamos un Layout para agrupar la entiqueta(TexView) y el campo (EditText)
        //de la Contraseña
        LinearLayout layoutPass = new LinearLayout(this);
        // Aplicamos los parámetros del layout a nuestros objectos
        layoutPass.setLayoutParams(params);
        layoutPass.addView(LabelPass);
        layoutPass.addView(inputPass);

        //Adicionamos al LAyout comun los dos nuevos layouts
        // el del Usuario y el de la Contraseña, creados anteriormente
        layout.addView(layoutUser);
        layout.addView(layoutPass);

        //Adicionamos el layout comun al helpBuilder
        helpBuilder.setView(layout);

        helpBuilder.setPositiveButton("OK",
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        // Do nothing but close the dialog
                        String inputUserTxt = inputName.getText().toString();
                        String inputPasswordTxt = inputPass.getText().toString();

                        if (inputUserTxt.equals(userName) & inputPasswordTxt.equals(password)) {
                            //Seleccionamos la acción a ejecutar
                            loginAction(action);

                        } else {
                            Toast toast = Toast.makeText(getBaseContext(), "Usuario o Contraseña no válidos. Inténtelo otra vez", Toast.LENGTH_LONG);
                            toast.setGravity(Gravity.CENTER, 0, 0);
                            toast.show();
                            // Si la contraseña o el usuario no son válidos volvemos a motrar el diálogo
                            showAccessPopUpDialog(userNamePreferences,passwordPreferences,action);
                        }
                    }
                });

        helpBuilder.setNegativeButton("Cancelar", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // Do nothing
                finish();
            }
        });
        // Crear el diálogo no lo muestra, hay que hacer el show()
        AlertDialog helpDialog = helpBuilder.create();
        // Deshabilitar hacer click fuera del diálogo
        helpDialog.setCanceledOnTouchOutside(false);
        //Mostrar el diálogo
        helpDialog.show();
        //Establecer ancho y alto
        helpDialog.getWindow().setLayout(400, 410);
    }
Procedimiento: loginAction(...)
    protected boolean loginAction(int action){
        switch (action) {
            case LOGIN_ACTION:
                validUser = true;
                Toast toast = Toast.makeText(getBaseContext(), "USUARIO Y CONTRASEÑA CORRECTOS. (:", Toast.LENGTH_LONG);
                toast.setGravity(Gravity.CENTER, 0, 0);
                toast.show();
                return true;
            case SETTINGS_ACTION:
                //Preferences access
                startActivity(new Intent(getApplicationContext(), Preferences.class));
                return true;
            default:
                return false;
        }
    } 
Para descargar el código fuente de la aplicación: LoginTutorial

Recordar que para acceder por primera vez debe teclear
USUARIO: admin
CONTRASEÑA: 1234 

No hay comentarios: