miércoles, 21 de noviembre de 2012

Encriptar/Desencriptar datos en 4.2

En algunas ocasiones nos puede interesar encriptar datos para aumentar la seguridad de nuestra aplicación. Para ello podemos usar la siguiente clase que funciona hasta la versión 4.1.2 de Android:

/**
 * Util class to perform encryption/decryption over strings.
 */
public final class UtilsEncryption
{
    /** The logging TAG */
    private static final String TAG = UtilsEncryption.class.getName();

    /** */
    private static final String KEY = "some_encryption_key";

    /**
     * Avoid instantiation. <br/>
     */
    private UtilsEncryption()
    {
    }

    /** The HEX characters */
    private final static String HEX = "0123456789ABCDEF";

    /**
     * Encrypt a given string. <br/>
     * 
     * @param the string to encrypt
     * @return the encrypted string in HEX
     */
    public static String encrypt( String cleartext )
    {
        try
        {
            byte[] result = process( Cipher.ENCRYPT_MODE, cleartext.getBytes() );
            return toHex( result );
        }
        catch ( Exception e )
        {
            System.out.println( TAG + ":encrypt:" + e.getMessage() );
        }
        return null;
    }

    /**
     * Decrypt a HEX encrypted string. <br/>
     * 
     * @param the HEX string to decrypt
     * @return the decrypted string
     */
    public static String decrypt( String encrypted )
    {
        try
        {
            byte[] enc = fromHex( encrypted );
            byte[] result = process( Cipher.DECRYPT_MODE, enc );
            return new String( result );
        }
        catch ( Exception e )
        {
            System.out.println( TAG + ":decrypt:" + e.getMessage() );
        }
        return null;
    }


    /**
     * Get the raw encryption key. <br/>
     * 
     * @param the seed key
     * @return the raw key
     * @throws NoSuchAlgorithmException
     */
    private static byte[] getRawKey()
        throws NoSuchAlgorithmException
    {
        KeyGenerator kgen = KeyGenerator.getInstance( "AES" );
        SecureRandom sr = SecureRandom.getInstance( "SHA1PRNG" );
        sr.setSeed( KEY.getBytes() );
        kgen.init( 128, sr );
        SecretKey skey = kgen.generateKey();
        return skey.getEncoded();
    }

    /**
     * Process the given input with the provided mode. <br/>
     * 
     * @param the cipher mode
     * @param the value to process
     * @return the processed value as byte[]
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     */
    private static byte[] process( int mode, byte[] value )
        throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException,     NoSuchAlgorithmException,
        NoSuchPaddingException
    {
        SecretKeySpec skeySpec = new SecretKeySpec( getRawKey(), "AES" );
        Cipher cipher = Cipher.getInstance( "AES" );
        cipher.init( mode, skeySpec );
        byte[] encrypted = cipher.doFinal( value );
        return encrypted;
    }

    /**
     * Decode an HEX encoded string into a byte[]. <br/>
     * 
     * @param the HEX string value
     * @return the decoded byte[]
     */
    protected static byte[] fromHex( String value )
    {
        int len = value.length() / 2;
        byte[] result = new byte[len];
        for ( int i = 0; i < len; i++ )
        {
            result[i] = Integer.valueOf( value.substring( 2 * i, 2 * i + 2 ), 16     ).byteValue();
        }
        return result;
    }

    /**
     * Encode a byte[] into an HEX string. <br/>
     * 
     * @param the byte[] value
     * @return the HEX encoded string
     */
    protected static String toHex( byte[] value )
    {
        if ( value == null )
        {
            return "";
        }
        StringBuffer result = new StringBuffer( 2 * value.length );
        for ( int i = 0; i < value.length; i++ )
        {
            byte b = value[i];

            result.append( HEX.charAt( ( b >> 4 ) & 0x0f ) );
            result.append( HEX.charAt( b & 0x0f ) );
        }
        return result.toString();
    }
}

Para qué la clase funcione a partir de la 4.2 tenemos que sustituir el siguiente código de la clase anterior:

     SecureRandom sr = SecureRandom.getInstance( "SHA1PRNG", "Crypto" );

Este problema es debido a los providers que usa Google en la 4.2. Hasta la 4.1.2 se usa Crypto ya que si hacemos ran.getProvider podemos verlo. A partir de la 4.2 si ejecutamos la misma instrucción nos devuelve AndroidOpenSSL.

Espero que les haya servido.

It works!
Roger Sala,



sábado, 10 de noviembre de 2012

StrictMode

El StrictMode nos permite analizar nuestra aplicación en tiempo de ejecución. Esta disponible a partir de la API 9. Con su uso podemos detectar:
  • Escrituras/lecturas en disco.
  • Uso de la red
  • Violación: log/crash, dropbox, cuadros de diálogos que pueden perjudicar el uso de la aplicación (que sea molesto visitar nuestra app por culpa de los cuadros de dialógos).
A nadie le gusta descargarse una aplicación y tener que esperar 7 segundos para poder realizar una primera acción. A nadie le gusta tener un Progress Dialog y no poder usar la aplicación a su vez. StrictMode, nos ayuda a detectar estos casos y así, podemos mejorar el uso de nuestra aplicación.
Si quieren leer más sobre StrictMode aquí les dejo un post de Android Developers.

A continuación el código a escribir para usar StrictMode:

1.- Con la configuración por defecto:

public void onCreate(Bundle savedInstanceState) {
        if (DEVELOPER_MODE) {
        StrictMode.enableDefaults();
        }
        super.onCreate(savedInstanceState);
}

2.- Con la configuración personalizada:

public void onCreate(Bundle savedInstanceState) {
        if (DEVELOPER_MODE) {
              StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
             .detectDiskReads()
             .detectDiskWrites()
             .detectNetwork()
             .penaltyLog()
             .build());

        }
        super.onCreate(savedInstanceState);
}

IMPORTANTE: Usarlo solo cuando esten debugando o provando la aplicación. Deshabilitarlo para subirla en el Store. Para eso se usa la variable DEVELOPER_MODE.



lunes, 5 de noviembre de 2012

Custom Loading

Si deseamos personalizar los elementos de cargando de nuestra aplicación podemos tan solo tenemos que crear nuestro propio diseño y sobreescribir el de Android. En este post vamos a ver como se realiza dicha acción: El diseño esta hecho a partir de flechas pero cada uno puedo poner lo que desee y el número de elementos que prefiera (aquí solo hay cuatro pero se pueden poner más o menos).

A continuación el código:
res/anim/custom_dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:drawable="@drawable/right"
        android:duration="150"/>
    <item
        android:drawable="@drawable/up"
        android:duration="150"/>
    <item
        android:drawable="@drawable/left"
        android:duration="150"/>
    <item
        android:drawable="@drawable/down"
        android:duration="150"/>
</animation-list>

activity_loading.xml
<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" >

</RelativeLayout>




LoadingActivity.java

package com.example.blogcustomdialoanimation;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.view.Menu;

public class LoadingActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_loading);
        
        ProgressDialog dialog = new ProgressDialog(this);
        dialog.setIndeterminate(true);
        dialog.setIndeterminateDrawable(getResources().getDrawable(R.anim.custom_dialog));
        dialog.setMessage("Some Text");
        dialog.show();
        
    }

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

A continuación les dejo las imagenes:




It works!
Roger Sala,

jueves, 1 de noviembre de 2012

ViewFlipper estático y dinámico

Este Widget nos permite cambiar de una vista a otra de forma automática y, si se desea, aplicando animaciones. Por ejemplo:
- Podemos crear un marco de foto digital que cada X segundos vayan cambiando las fotos.
- Podemos añadir nuestra propia publicidad y que vaya cambiando el anuncio cada X segundos.
- Y otros...

En estes post vamos a ver como añadir en nuestro proyecto un ViewFlipper, ya sea con contenido estático, es decir, en el layout definimos el número máximo de elementos que puede tener. O bien, con contenido dinámico, es decir, en nuestra Activity le vamos añadiendo vistas.
Antes de empezar, destacar, que para el uso de un ViewFlipper se recomienda un mínimo de 2 vistas (layout, imageview, textview, etc.). Así pues, aquí les dejo el código:

res/anim/push_in_left.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2007 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
          http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:duration="700"
        android:fromXDelta="100%p"
        android:toXDelta="0" />

    <alpha
        android:duration="700"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />

</set>


res/anim/push_out_left.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2007 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
          http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:duration="700"
        android:fromXDelta="0"
        android:toXDelta="-100%p" />

    <alpha
        android:duration="700"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />

</set>


activity_flipper.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/linear_layout_highlighted"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:orientation="vertical" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dip"
        android:text="Static Flipper with push animation" />

    <ViewFlipper
        android:id="@+id/flipper_static"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dip"
        android:flipInterval="2000" >

        <ImageView
            android:id="@+id/imageview_highlighted_1"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitCenter"
            android:src="@drawable/android_one" />

        <ImageView
            android:id="@+id/imageview_highlighted_2"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:scaleType="fitCenter"
            android:src="@drawable/android_two" />
    </ViewFlipper>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dip"
        android:text="Dynamic Flipper" />

    <ViewFlipper
        android:id="@+id/flipper_dynamic"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dip"
        android:flipInterval="4000" >
    </ViewFlipper>

</LinearLayout>

FlipperActivity.java
package com.example.blogviewflipper;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.ViewFlipper;

public class FlipperActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flipper);

// static flipper
ViewFlipper mStaticFlipper = ((ViewFlipper) findViewById(R.id.flipper_static));
mStaticFlipper.startFlipping();
mStaticFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.push_in_left));
mStaticFlipper.setOutAnimation(AnimationUtils.loadAnimation(this,
R.anim.push_out_left));

// dynamic flipper
ViewFlipper mDynamicFlipper = ((ViewFlipper) findViewById(R.id.flipper_dynamic));

ImageView image = new ImageView(this);
image.setImageResource(R.drawable.android_one);
image.setScaleType(ScaleType.FIT_CENTER);
mDynamicFlipper.addView(image);

image = new ImageView(this);
image.setImageResource(R.drawable.android_two);
image.setScaleType(ScaleType.FIT_CENTER);
mDynamicFlipper.addView(image);

image = new ImageView(this);
image.setImageResource(R.drawable.android_three);
image.setScaleType(ScaleType.FIT_CENTER);
mDynamicFlipper.addView(image);

mDynamicFlipper.startFlipping();
}

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

}

A continuación les dejo una imagen, dónde se muestra el contenido:



It works!
Roger Sala,