miércoles, 16 de octubre de 2013

Conexión a internet y su velocidad

Cómo manejar las conexiones de datos.
- Detectar el tipo de connexión.
- También nos permite detectar la velocidad de la conexión.

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.telephony.TelephonyManager;


public class Connectivity {

    /**
     * Get the network info
     * @param context
     * @return
     */
    public static NetworkInfo getNetworkInfo(Context context){
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        return cm.getActiveNetworkInfo();
    }

    /**
     * Check if there is any connectivity
     * @param context
     * @return
     */
    public static boolean isConnected(Context context){
        NetworkInfo info = Connectivity.getNetworkInfo(context);
        return (info != null && info.isConnected());
    }

    /**
     * Check if there is any connectivity to a Wifi network
     * @param context
     * @param type
     * @return
     */
    public static boolean isConnectedWifi(Context context){
        NetworkInfo info = Connectivity.getNetworkInfo(context);
        return (info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_WIFI);
    }

    /**
     * Check if there is any connectivity to a mobile network
     * @param context
     * @param type
     * @return
     */
    public static boolean isConnectedMobile(Context context){
        NetworkInfo info = Connectivity.getNetworkInfo(context);
        return (info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_MOBILE);
    }

    /**
     * Check if there is fast connectivity
     * @param context
     * @return
     */
    public static boolean isConnectedFast(Context context){
        NetworkInfo info = Connectivity.getNetworkInfo(context);
        return (info != null && info.isConnected() && Connectivity.isConnectionFast(info.getType(),info.getSubtype()));
    }

    /**
     * Check if the connection is fast
     * @param type
     * @param subType
     * @return
     */
    public static boolean isConnectionFast(int type, int subType){
        if(type==ConnectivityManager.TYPE_WIFI){
            return true;
        }else if(type==ConnectivityManager.TYPE_MOBILE){
            switch(subType){
            case TelephonyManager.NETWORK_TYPE_1xRTT:
                return false; // ~ 50-100 kbps
            case TelephonyManager.NETWORK_TYPE_CDMA:
                return false; // ~ 14-64 kbps
            case TelephonyManager.NETWORK_TYPE_EDGE:
                return false; // ~ 50-100 kbps
            case TelephonyManager.NETWORK_TYPE_EVDO_0:
                return true; // ~ 400-1000 kbps
            case TelephonyManager.NETWORK_TYPE_EVDO_A:
                return true; // ~ 600-1400 kbps
            case TelephonyManager.NETWORK_TYPE_GPRS:
                return false; // ~ 100 kbps
            case TelephonyManager.NETWORK_TYPE_HSDPA:
                return true; // ~ 2-14 Mbps
            case TelephonyManager.NETWORK_TYPE_HSPA:
                return true; // ~ 700-1700 kbps
            case TelephonyManager.NETWORK_TYPE_HSUPA:
                return true; // ~ 1-23 Mbps
            case TelephonyManager.NETWORK_TYPE_UMTS:
                return true; // ~ 400-7000 kbps
            /*
             * Above API level 7, make sure to set android:targetSdkVersion
             * to appropriate level to use these
             */
            case TelephonyManager.NETWORK_TYPE_EHRPD: // API level 11
                return true; // ~ 1-2 Mbps
            case TelephonyManager.NETWORK_TYPE_EVDO_B: // API level 9
                 return true; // ~ 5 Mbps
            case TelephonyManager.NETWORK_TYPE_HSPAP: // API level 13
                return true; // ~ 10-20 Mbps
            case TelephonyManager.NETWORK_TYPE_IDEN: // API level 8
                 return false; // ~25 kbps
            case TelephonyManager.NETWORK_TYPE_LTE: // API level 11
               return true; // ~ 10+ Mbps
            // Unknown
            case TelephonyManager.NETWORK_TYPE_UNKNOWN:
            default:
                return false;
            }
        }else{
            return false;
        }
    }

}

It works!

lunes, 7 de octubre de 2013

Dalvik messages

//TODO: Lo traduciré en breve.

GC_FOR_MALLOC means that the GC was triggered because there wasn't enough memory left on the heap to perform an allocation. Might be triggered when new objects are being created.
GC_EXPLICIT means that the garbage collector has been explicitly asked to collect, instead of being triggered by high water marks in the heap. Happens all over the place, but most likely when a thread is being killed or when a binder communication is taken down.
There are a few others as well:
GC_CONCURRENT Triggered when the heap has reached a certain amount of objects to collect.
GC_EXTERNAL_ALLOC means that the the VM is trying to reduce the amount of memory used for collectable objects, to make room for more non-collectable.

Example:
GC_CONCURRENT freed <1K, 14% free 21220K/24455K, paused 6ms+26ms
  • GC_CONCURRENT : Jumps in because Heap is full
  • 14% free 21220K/24455K After this collection 14% of memory is free. Check the heap usage.
  • paused 6ms+26ms Time taken to collect garbage.

miércoles, 12 de diciembre de 2012

EditText: maxLength por Código

Por si alguna vez habéis tenido algún problema/duda con definir el tamaño máximo soportado por los EditText o TextView vía código aquí tenéis la solución. Simple pero funcional:

private void editTextMaxLength(EditText ed, int maxLength){
      InputFilter[] filterArray = new InputFilter[1];
      filterArray[0] = new InputFilter.LengthFilter(maxLength);
      ed.setFilters(filterArray);
}

It works!
Roger Sala,

martes, 4 de diciembre de 2012

Google Maps API v2. MapFragment

Por fin!

Los mapas y los Fragments ya se ha unificado. A continuación y sin andarse mucho por las ramas el ejemplo de como añadirlo y como manejar el GoogleMaps.

1.- Necesitamos la API Key que se obtiene de forma distinta a los MapActivity. En el siguiente enlace se detallan los pasos de como obtenerla. Obtener API KEY

2.- Descargar del SDK Manager -> Extras -> Google Play services.
3.- Importar en libs el archivo jar. AndroidSDK -> Extras -> Google -> Google Play Services -> libs -> google-play-services.jar

4.- Ya podemos empezar a generar el código.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.maps.fragments"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="16" />

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
   
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <permission
        android:name="com.example.maps.fragments.permission.MAPS_RECEIVE"
        android:protectionLevel="signature" />
    <uses-permission android:name="com.example.maps.fragments.permission.MAPS_RECEIVE" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.maps.fragments.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <!-- API KEY  -->
        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="AIzaSyA-v3ukHyXbPP0db9khnT6pwzNzMfM79Nw" />
    </application>

</manifest>



MainActivity.java

package com.example.maps.fragments;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class MainActivity extends Activity implements OnClickListener {

private GoogleMap mMap;
private static final LatLng MY_POINT = new LatLng(41.66, 1.54);

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// Init view elements
((Button) findViewById(R.id.none)).setOnClickListener(this);
((Button) findViewById(R.id.satellite)).setOnClickListener(this);
((Button) findViewById(R.id.hybrid)).setOnClickListener(this);
((Button) findViewById(R.id.normal)).setOnClickListener(this);
((Button) findViewById(R.id.terrain)).setOnClickListener(this);

// Verify Map availability
setUpMapIfNeeded();

}

private void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the
// map.
if (mMap == null) {
mMap = ((MapFragment) getFragmentManager().findFragmentById(
R.id.map)).getMap();
// Check if we were successful in obtaining the map.
if (mMap != null) {
// addMarkers
addMarkers();
addCameraOption();
}
}
}

private void addMarkers() {
//custom icon
Marker my_marker = mMap.addMarker(new MarkerOptions()
.position(MY_POINT).title("Melbourne")
.snippet("Some informaton: eg: Population: 4,137,400")
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher)));

//default icon
// Marker my_marker = mMap.addMarker(new MarkerOptions()
// .position(MY_POINT).title("Catalunya")
// .snippet("Some informaton: eg: Population: 4,137,400"));

}

private void addCameraOption(){
// Move the camera instantly to Sydney with a zoom of 15.
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(MY_POINT, 15));

// Zoom in, animating the camera.
mMap.animateCamera(CameraUpdateFactory.zoomIn());

// Zoom out to zoom level 10, animating with a duration of 2 seconds.
// mMap.animateCamera(CameraUpdateFactory.zoomTo(10), null, 2000);

// Construct a CameraPosition focusing on Mountain View and animate the camera to that position.
CameraPosition cameraPosition = new CameraPosition.Builder()
   .target(MY_POINT)      // Sets the center of the map to Mountain View
   .zoom(17)                   // Sets the zoom
   .bearing(90)                // Sets the orientation of the camera to east
   .tilt(30)                   // Sets the tilt of the camera to 30 degrees
   .build();                   // Creates a CameraPosition from the builder
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}


@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.hybrid:
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
break;
case R.id.terrain:
mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
break;
case R.id.none:
mMap.setMapType(GoogleMap.MAP_TYPE_NONE);
break;
case R.id.normal:
mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
break;
case R.id.satellite:
mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
break;

default:
break;
}
}

}


activity_main.xml

<LinearLayout 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"
    android:orientation="vertical" >

    <!-- <fragment -->
    <!-- android:name="com.example.maps.fragments.TopFragment" -->
    <!-- android:layout_width="fill_parent" -->
    <!-- android:layout_height="100dip" -->
    <!-- android:tag="topFragment" /> -->

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:weightSum="5" >

        <Button
            android:id="@+id/normal"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Normal"
            android:textSize="12sp" />

        <Button
            android:id="@+id/hybrid"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Hybrid"
            android:textSize="12sp" />

        <Button
            android:id="@+id/satellite"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Satellite"
            android:textSize="12sp" />

        <Button
            android:id="@+id/terrain"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Terrain"
            android:textSize="12sp" />

        <Button
            android:id="@+id/none"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="None"
            android:textSize="12sp" />
    </LinearLayout>

    <fragment
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.MapFragment"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1" />

</LinearLayout>

Aquí tenéis una imagen del MapFragment con el marker personalizado, y con un zoom personalizado de la camera:



It works!
Roger Sala,

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,