martes, 15 de septiembre de 2015

Eliminar una fila de TableLayoutPanel en Windows Forms C#

Hola muy buenas.

Para aquellos que estén trabajando con TableLayoutPanel de Windows Forms, os traigo un método que nos permite eliminar una fila de la tabla con la que estemos trabajando.

Buscando por Internet, he encontrado muchos ejemplos de los cuales me he basado, pero ninguno de ellos funcionan al 100%. Este es más genérico y lo he utilizado para mi trabajo y creo que puede ser de ayuda para cualquiera.


public static void RemoveRowTableLayout(System.Windows.Forms.TableLayoutPanel tableLayoutPanel, int rowNumber)
{

 var controlesTotal = Enumerable.Range(0, tableLayoutPanel.ColumnCount).Select(x => new Tuple(tableLayoutPanel.GetControlFromPosition(x, rowNumber), x)).ToArray;

 for (index = 0; index <= controlesTotal.Length - 1; index++) {
  dynamic item = controlesTotal(index);

  if ((item.Item1 != null)) {
   tableLayoutPanel.Controls.Remove(item.Item1);
  }
 }

 foreach (Control control in tableLayoutPanel.Controls) {
  int row = tableLayoutPanel.GetRow(control);
  if (row > rowNumber) {
   tableLayoutPanel.SetRow(control, row - 1);
  }
 }

 tableLayoutPanel.RowStyles.RemoveAt(rowNumber);
 tableLayoutPanel.RowCount = tableLayoutPanel.RowCount - 1;

}

Una posible mejora puede ser transformar el método a un método extensor para que pueda ser encapsulado dentro de la misma clase del control tabla.

sábado, 18 de julio de 2015

¿Porqué no Comprar electrodomésticos Balay?

La razón por el cual no es aconsejable comprar ningún tipo de electrodoméstico de esta marca es porque aplican la mala práctica de programar sus productos para que se averíen una vez que han cumplido su garantía (Video 1 y Video 2). A este tipo de estafa, engaño o robo se le conoce como obsolescencia programada. En este programa de TVE1 se explica mejor el fenómeno al que los consumidores indefensos estamos condenados y que ninguna  organización que protege a los consumidores denuncia este tipo de estafa.


Personalmente he sufrido este atraco a mi bolsillo con un frigorífico modelo 3KRP7767,  el cual se ha averiado justo al cumplir la garantía. 





No es casualidad que otros clientes de Balay, hayan sido víctimas de tal estafa como se puede apreciar en el siguiente enlace Opiniones del Canal Mundo Balay de Youtube. Al parecer todos tenemos el mismo problema detectado en los mismos componentes y en el mismo periodo de tiempo tras realizar la compra. Balay piensa que somos seres indefensos y separados y por ello dedica recursos a realizar esta práctica. ¿Cómo nos podemos defender los consumidores? Pues muy sencillo, difundiendo el conocimiento por todos los canales posibles (Faceboock, Firma Emails, youtube, etc.) con el fin de informar a posibles compradores de electrodomésticos Balay, sobre el problema al que se enfrentarán cuando estos cumplan el tiempo de garantía.

Otra forma de defensa puede ser aportar información sobre como arreglar estos electrodomésticos sin tener que acudir a sus servicios técnicos y por ende pasar por caja, ya que con estas malas prácticas es lo que justamente pretenden.


Para ello necesitamos tener unos conocimientos básicos de electrónica que se pueden adquirir por youtube.



En el caso de mi frigorífico BALAY 3KRP7767, el fallo comenzó cuando una madrugada de verano en Sevilla, empieza la alarma del frigorífico a sonar indicando que el congelador está perdiendo fuerza. En ese momento me despierto y compruebo que el frigorífico a pesar de estar encendido parece enfriar. Inmediatamente pulso los botones de turbo para obligar al compresor a funcionar y nada de nada. Obviamente el frigorífico estaba KO y obviamente toda mi comida ya se había estropeado.

Al día siguiente llamo al servicio de BALAY y compruebo que mi frigorífico había pasado  justamente la garantía de 2 años dos o tres meses antes. ¿Como puede ser que tras cumplir la garantía este fallando? Pues muy sencillo, al llamar al servicio técnico me proponen enviar a mi casa un técnico para a coste de 50€ la visita mas luego el coste del arreglo.


Inmediatamente empiezo a buscar por Internet, y encuentro que el problema principal de que se encuentran los usuarios de BALAY es que balay incorpora en el módulo de control un condensador chino de mala calidad, el cual viene a caducar y estropearse una vez pasada la garantía. Los usuarios se quejan que los técnicos de BALAY no sustituyen dicho condensador que sólo cuesta 3€ y si que sustituyen el módulo completo cobrando al cliente 100€ o incluso 150€ además de la visita. Total si hacemos cuentas, no interesa ya que un frigorífico nuevo cuesta alrededor de 300€ o 400€ y tenemos dos años de garantía mientras que una reparación sólo tiene 6 meses de garantía.


Después de buscar en varios foros de Internet, encontré información sobre el posible fallo que pudiese tener mi frigorífico y encontré que el principal problema era que suelen fallar un condensador que no cuesta mas de 3 euros. Curiosamente, he estado buscando esta información nuevamente para colocarla en este artículo y al parecer han censurado los distintos foros donde se explicaba el problema y la solución. Es una pena porque había disponible imágenes detalladas sobre donde localizar dicho condensador. No sería nada de extrañar que el motivo de la censura sea que los fabricantes hayan tomado medidas para que esta información no sea transmitida a los usuario y por tanto burlarnos de la obsolescencia programada.

En mi caso llamé a un técnico que no me cobraba la reparación al no se que funcionase y descubrimos que el módulo de control no era el mismo que en la documentación encontrada en Internet. 


El módulo que tiene este frigorífico es exactamente el mismo que el que tienen los frigoríficos de los siguientes modelos:

BALAY, 3KFP7665
BALAY, 3KFP7865-01
BALAY, FD: 8905 00230
BOSCH, KGN36A03-01
BOSCH, KGN36A75-09
BOSCH, KGN39A71-01
BOSCH, KGN39A73-08
BOSCH, KGN39A73-16
BOSCH, KGN46A71-03
BOSCH, KGN49A71-03
SIEMENS, KG39NA71
SIEMENS, KG49NA71

La etiqueta y el modelo que figura en la placa es el siguiente:



Tras buscar por Internet por alguna de sus referencias no encontramos información alguna. Sin embargo buscando por imágenes relacionadas encontramos la siguiente información:

Modulo electrónico frigorífico Siemens, 9000 621 550 , EPK64885, 220-240V

Cambiamos por precaución los condensadores y el frigorífico seguía fallando.


Video 1 y Video 2

Entonces enviamos la placa a un amigo del técnico que nos comentó que el fallo podría ser de los revés que se ven en la foto anterior.


El relé original son los dos blancos al cual muestro con mas detalle en la siguiente foto.




El electrónico cambió de ellos (el que figura de color negro) y el frigorífico empezó a funcionar sin problemas.






La información técnica sobre los relay los podemos encontrar en esta web. Relay HF3FD


Normalmente arreglar estos aparatos es muy barato, ya que los componentes electrónicos son muy baratos. La cuestión es saber determinar que pieza es la que falla.


Ahora le ha surgido otro nuevo problema y es que después de haber conseguido que el frigorífico funcione (congela y enfría) a los 3 o 4 meses de funcionamiento, el congelador hace demasiado hielo y obstruye el conducto que envía el frío hacia el refrigerador y este no enfría.


Este problema es generalizado en otros frigoríficos como podemos ver en el siguiente vídeo


El problema ahora es determinar porqué se produce esta obstrucción e intentar arreglarlo.

Las siguientes imágenes son imágenes detalladas del módulo de control, las cuales las aporto por si alguien sabe descifrar el circuito y aportar información. Toda la información que me remitáis por comentarios será bien recibidas e intentaremos ir ampliando el artículo entre todos para que este tipo de problema lo podamos solventar al menor coste posible.

A medida que siga encontrando mas información iré ampliando el artículo.

Muchas gracias y espero vuestra aportación.

PD: Me gustaría que alguien me dijese que función tienen el relé blanco, ya que el negro sabemos que sirve para que arranque el compresor.


















Relé sustituido


















miércoles, 28 de enero de 2015

Plantilla para crear clases Proxy o decorador en C# de forma aútomática

Hola a todos. Hace tiempo que no escribo en mi blog y por ello pido disculpas. Sin embargo, como he comentado otras veces, prefiero no escribir demasiado pero producir contenido que considero que puede ser de calidad.

El trabajo y la vida personal no me deja demasiado tiempo para poder escribir todas las entradas que me gustaría en el blog, sin embargo, sí que escribo ésta por que en el ámbito profesional me he encontrado con un problema que podía resolverse con una sencilla plantilla que nos ayude a crear clases de código de forma automática.

El problema que intento resolver es aquel que surge cuando se dispone de un objeto ya relleno por algún sistema o componente software como puede ser por ejemplo el componente de persistencia Microsoft Entity Framework o cualquier otro componente de terceros. Estos objetos, que ya vienen rellenos, no pueden ser heredados por otra clase que nosotros hagamos puesto que no tenemos el control de poder rellenar dichos objetos. Por ejemplo, cuando recuperamos un registro de base de datos en forma de objetos utilizando Microsoft Entity Framework no podemos rellenar el objeto puesto que este tomará los valores almacenados en base de datos. Para resolver este problema existe un patrón de diseño llamado decorador o proxy.


Implementación:

       

private class ElementoProxy : Elemento
{
    // Incluímos una referencia a otro elemento.
    private Elemento elemento;
 
    // Inyectamos el elemento a través del constructor.
    public ElementoProxy(Elemento elemento)
    {
        this.elemento = elemento;
    }
 
    // El método HttpGet realizará comprobaciones y/o adaptaciones para
    // posteriormente realizar la llamada al método homónimo del objeto real
    public string HttpGet(string uri)
    {
        if (uri.ToLower().Contains("paginaprohibida.com"))
            return null;
        else
            return HttpGet(uri);
    }
};

      
 

Con esta idea también podemos implementar herencia múltiple que no está permitida en los lenguajes de programación orientada a objetos, sin embargo, este patrón nos permitiría dicha herencia múltiple o al menos simular.

En Internet encontramos numerosos ejemplos de cómo implementar un patrón proxy a partir de un objeto ya relleno, sin embargo estos ejemplos encontrados  implementan dicho patrón de forma dinámica en tiempo de ejecución y no en tiempo de diseño de código por lo que perdemos el control a la hora de poder extender nuestras clases proxy que puedan implementar dicho patrón de diseño.

Para poder extender o realizar una clase proxy en la que podamos introducir un objeto ya relleno y extenderlo a nuestras necesidades, la mejor solución que se me ha ocurrido ha sido implementar una plantilla de tipo T4. Dicha plantilla sería capaz de analizar por reflexión el objeto y generar códigos de forma automática que implemente mediante código un objeto proxy para englobar cualquier objeto que necesitemos extender y no podamos, o simplemente, no nos interese instanciar de nuevo. (Objetos definidos en componentes de terceros con constructores privados u objetos que a pesar de tener constructores públicos son rellenados por componentes de los cuales no tenemos control).

La solución que planteo es la siguiente:

Las plantillas de tipo T4 generan un fichero de texto con contenido de código fuente. Este fichero en general es único para cada plantilla, de manera que si nosotros queremos que una misma plantilla T4 genere varios ficheros de código fuente necesitaremos parametrizar este comportamiento e indicar de alguna forma cuántos ficheros de código fuente genera cada uno de éstos. En nuestra solución cada fichero implementará una clase proxy distinta. Debido a que estos ficheros son generados de forma automática, no es recomendable modificar los ya generados. Para complementar estas clases proxy se recomienda crear otro fichero que represente la misma clase generada (el mismo nombre de la clase) indicando que dicha clase es parcial. De hecho, la plantilla genera la clase proxy como clase parcial posibilitando su reimplementación complementaria en cualquier otro fichero evitando así que nuestro código no sea machado o destruido.

Por otro lado, debemos indicar cuál será la clase base o interfaz que queremos extender. Es obligatorio indicar a la plantilla donde está situada dicha clase base o interfaz para que ésta pueda leer su estructura e implementación mediante mecanismos de reflexión. Por supuesto, debemos especificar  el fichero de ensamblado donde se encuentra ya compilada la clase o interfaz a extender.

La manera más sencilla y eficaz de realizar esta parametrización es mediante un fichero XML el cual nos permitirá indicar de forma clara y sencilla dicha información para que la plantilla T4 pueda tomar estos parámetros y realizar su trabajo.

La estructura de este fichero de parámetros XML es la que propongo en el siguiente fragmento de código.  En éste podemos observar que se indica cuál será el nombre de la clase o interfaz que se quiere extender, el nombre de la clase nueva que se extenderá, así como el fichero de ensamblado que contiene la clase base en la cual se apoyará la plantilla de T4 para realizar su trabajo.


       

            
<?xml version="1.0" encoding="utf-8" ?>
<generador>
  <proxyclass nombreCompletoBase="System.Text.StringBuilder"

 nombre ="MiPaquete.StringBuilderProxy"

 assembly="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1\mscorlib.dll"></proxyclassu>
</generador>

       
 

Debemos tener en cuenta que la ruta del ensamblado debe ser la ruta completa del mismo.

Ahora toca hablar sobre la implementación de la plantilla.

El siguiente fragmento de código mostrará el código correspondiente a la plantilla T4 en la que se pueden diferenciar varias partes:

  1. En la primera de ellas se hace mención a la importación de ensamblados necesarios para poder tratar con ficheros XML. También se hace la inclusión de los paquetes necesarios para poder leer y escribir fichero de texto, la lectura y tratamiento de ficheros XML. Así mismo, se incluyen los paquetes necesarios para realizar las labores de reflexión.
  2. En la segunda parte del fragmento de código se realiza la lectura del fichero XML al cual le hemos llamado "proxygenerador.xml". Debemos tener en cuenta que cuando Visual Studio ejecuta la plantilla tipo T4, ésta la realiza sobre la carpeta principal de la solución que contiene el proyecto y no donde tengamos alojada la plantilla tipo T4. En mi caso he alojado el fichero de parámetros XML en la misma carpeta de paquetes que la plantilla de tipo T4, pero la ruta del fichero XML podría ser distinta, aunque no lo aconsejo porque cambiar dicha ruta llevaría a reimplementar la plantilla. Al ejecutar la plantilla T4 ésta no encontrará el fichero XML en la misma carpeta donde está alojada la plantilla, por tanto, para resolver este problema necesitamos introducir un mecanismo para que sea capaz de encontrar el fichero XML. Este mecanismo necesita incluir en la cabecera de la plantilla un parámetro "hostspecific" con valor acierto. Posteriormente tendremos que resolver la ruta del fichero XML utilizando el método "ResolvePath" cuyo valor devuelto será pasado a la clase especializada en leer documentos XML.
  3. El siguiente paso será por tanto hacer un "parser" sobre el propio fichero XML y extraer la información que dicho fichero contiene y que se ha visto en el fragmento de código anterior.
    • Por cada etiqueta en la que se especifique los parámetros para la generación de una clase proxy, se creará un fichero de código con la clase proxy deseada.
  4. Por último, en la parte final de la plantilla, es interesante destacar el método "GetFriendlyTypeName" el cual está especializado en la inferencia de los tipos que utilizan los argumentos, los tipos devueltos por las propiedades y tipos devueltos por los métodos de la clase base o interfaz que se quiere extender.

Tengo que reconocer que los métodos para generar e inferir estos tipos, así como el método utilizado para escribir los ficheros de código fuente que se pretenden generar, no son de cosecha propia. Los he localizado en Internet. Sin embargo siento no poder incluir las referencias de dichas localizaciones ya que después de tanto navegar por Internet he perdido dichas referencias. Pido disculpa a sus autores. Si las localizara de nuevo, éstas serán incluidas.

Por tanto el código resultante listo para ser ejecutado es el que muestro en el siguiente fragmento de código.

Plantilla t4

       

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#
 XDocument xdoc = XDocument.Load(this.Host.ResolvePath("ProxyGenerador.xml"));

foreach (XElement xElement in xdoc.Descendants("ProxyClass"))
{
    Assembly assem = Assembly.LoadFile(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(this.Host.ResolvePath("ProxyGenerador.xml")), xElement.Attribute("assembly").Value)));
    Type baseType = assem.GetType(xElement.Attribute("nombreCompletoBase").Value);

 string nombreClaseProxy=xElement.Attribute("nombre").Value;

 #>
using System;

// ReSharper disable CheckNamespace
namespace <#= baseType.Namespace #>
// ReSharper restore CheckNamespace
{
 public partial class <#= nombreClaseProxy #> : <#= GetFriendlyTypeName(baseType) #>
 {
  #region Atributos

  private readonly <#= GetFriendlyTypeName(baseType) #> _component;

  #endregion

  #region Propiedades

<#
  foreach (PropertyInfo propertyInfo in baseType.GetProperties())
  {
#>
  public <#= GetFriendlyTypeName(propertyInfo.PropertyType) #> <#= propertyInfo.Name #>
  {
<#
 if (propertyInfo.CanRead)
 {
#>
   get { return _component.<#= propertyInfo.Name #>; }
<#   
 }
#>
<#
 if (propertyInfo.CanWrite)
 {
#>
   set { _component.<#= propertyInfo.Name #> = value; }
<#  
 }
#>
  } 
<#
  }
  
#>

  #endregion

  #region Contructores

  public <#= nombreClaseProxy #>(<#= baseType.Name #> component)
        {
            if (component == null)
                throw new ArgumentNullException("component");
            _component = component;
        }

  #endregion

  #region Metodos

<#
  foreach (MethodInfo methodInfo in baseType.GetMethods())
            {
   if (methodInfo.IsSpecialName)
   {
    continue;
   }
#>
  public <#= methodInfo.ReturnType.FullName #> <#= methodInfo.Name #>(<#
for (int index = 0; index < methodInfo.GetParameters().Length; index++)
{
    ParameterInfo parameterInfo = methodInfo.GetParameters()[index];

     if (index>0)
                    {                       #>,<# 
                    }
     if (parameterInfo.ParameterType.IsByRef)
                    {  
     if (parameterInfo.IsOut)
                    {
                        #>out <# 
                    }
                    else if (parameterInfo.IsIn)
                    {
     #>in <# 
                    }
                    else
                    {
                        #>ref <# 
                    } 
                    }
#><#= GetFriendlyTypeName(parameterInfo.ParameterType).Replace("&","") #> <#= parameterInfo.Name #>
<#

}#>)
  {
   <#

if (!"System.Void".Equals(methodInfo.ReturnType.FullName))
                {
      #>return <# 
                }
#>_component.<#= methodInfo.Name #>(<#
    for (int index = 0; index < methodInfo.GetParameters().Length; index++)
    {
     ParameterInfo parameterInfo = methodInfo.GetParameters()[index];

          if (index>0)
         {                       #>,<# 
         }

         if (parameterInfo.ParameterType.IsByRef)
                    {     
     if (parameterInfo.IsOut)
                    {
                        #>out <# 
                    }
                    else if (parameterInfo.IsIn)
                    {
     #>in <# 
                    }
                    else
                    {
                        #>ref <# 
                    }                  
                    }
    #><#= parameterInfo.Name #>
    <#

    }#>);
  }
<#

                
            }
#>

  #endregion
 }
}
<#
 SaveOutput(string.Format("{0}.cs",nombreClaseProxy));
}


#>
<#+
  void SaveOutput(string outputFileName)
  {
      string templateDirectory = Path.GetDirectoryName(Host.TemplateFile);
      string outputFilePath = Path.Combine(templateDirectory, outputFileName);
      File.WriteAllText(outputFilePath, this.GenerationEnvironment.ToString()); 

      this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length);
  }
 public static string GetFriendlyTypeName(Type t)
 {
 string typeName = t.FullName.Split("`".ToCharArray())[0];
 var genericArgs = t.GetGenericArguments();
 if (genericArgs.Length > 0)
 {
  typeName += "<";
  foreach (var genericArg in genericArgs)
  {
   typeName += GetFriendlyTypeName(genericArg) + ", ";
  }
  typeName = typeName.TrimEnd(',', ' ') + ">";
 }
 return typeName;
 }
#>





       



jueves, 30 de mayo de 2013

Plantilla para crear cursores en T-SQL MS Sql Server 2008

Hola muy buenas,

Como ya sabéis, publico de muy tarde en tarde, por lo que pido disculpas. Sin embargo el contenido que quiero poner en mi blog, es de cosecha propia.

Bueno, esta vez voy a aportar un pequeño código en SQL que nos va a permitir generar cursores, tan solo con indicar el nombre de la tabla que queremos realizar el cursor.

La motivación de realizar este pequeño código, es debido a que el equipo de desarrollo software que lidero, tarda en realizar tareas repetitivas como crear cursores, sobre todo cuando son cursores sobre tablas con un número alto de columnas.
El código es el siquiente:
       
declare @nombreTabla as sysname;
DECLARE @ORDINAL_POSITION AS INT,
@COLUMN_NAME AS sysname,
@DATA_TYPE AS NVARCHAR (50),
@CHARACTER_MAXIMUM_LENGTH AS INT,
@IS_NULLABLE AS VARCHAR,
@COLUMN_DEFAULT AS NVARCHAR,
@listaColumnas AS NVARCHAR (4000),
@listaVariables AS NVARCHAR (4000);

set @nombreTabla='Catalogo'

DECLARE cTable CURSOR    FOR SELECT   ORDINAL_POSITION,
                 COLUMN_NAME,
                 DATA_TYPE,
                 CHARACTER_MAXIMUM_LENGTH,
                 IS_NULLABLE,
                 COLUMN_DEFAULT
        FROM     INFORMATION_SCHEMA.COLUMNS
        WHERE    TABLE_NAME = @nombreTabla
        ORDER BY ORDINAL_POSITION ASC;

SET @listaColumnas = '';

SET @listaVariables = '';

OPEN cTable;

-- Lectura de la primera fila del cursor
FETCH cTable INTO @ORDINAL_POSITION, @COLUMN_NAME, @DATA_TYPE, @CHARACTER_MAXIMUM_LENGTH, @IS_NULLABLE, @COLUMN_DEFAULT;

WHILE (@@FETCH_STATUS = 0)
    BEGIN
        IF @listaColumnas <> ''
            BEGIN
                SET @listaColumnas = @listaColumnas + ',';
                SET @listaVariables = @listaVariables + ',';
            END
        SET @listaColumnas = @listaColumnas + @COLUMN_NAME;
        SET @listaVariables = @listaVariables+ '@' + @COLUMN_NAME;
        IF @CHARACTER_MAXIMUM_LENGTH IS NOT NULL
            BEGIN
                PRINT 'DECLARE @' + @COLUMN_NAME + ' as ' + @DATA_TYPE + '(' + CONVERT (VARCHAR (10), @CHARACTER_MAXIMUM_LENGTH) + ')';
            END
        ELSE
            BEGIN
                PRINT 'DECLARE @' + @COLUMN_NAME + ' as ' + @DATA_TYPE;
            END
        FETCH cTable INTO @ORDINAL_POSITION, @COLUMN_NAME, @DATA_TYPE, @CHARACTER_MAXIMUM_LENGTH, @IS_NULLABLE, @COLUMN_DEFAULT;
    END
PRINT ''
PRINT 'DECLARE c'+@nombreTabla+' CURSOR    FOR SELECT '+ @listaColumnas+' from '+@nombreTabla;
PRINT ''
PRINT 'OPEN c'+@nombreTabla
PRINT 'FETCH c'+@nombreTabla+' INTO '+@listaVariables
PRINT ''
PRINT 'WHILE (@@FETCH_STATUS = 0)'
PRINT 'BEGIN'
PRINT ''
PRINT 'FETCH c'+@nombreTabla+' INTO '+@listaVariables
PRINT 'END'
PRINT ''
PRINT 'CLOSE c'+@nombreTabla
PRINT 'DEALLOCATE c'+@nombreTabla

-- Cierre del cursor
CLOSE cTable;

-- Liberar los recursos
DEALLOCATE cTable;


Espero que con esta aportación podáis incrementar vuestra productividad en los desarrollos.

lunes, 10 de septiembre de 2012

Guía práctica para ejecutar aplicaciones Android en Mac con BlueStacks


Hola muy buenas.

Escribo este artículo porque sé que existen muchas personas como yo, que desean ejecutar aplicaciones Android en sus Mac’s, y por lo que he estado leyendo, es algo bastante complicado de conseguir.

El motivo por el cual surge la necesidad de ejecutar dichas aplicaciones, como en mi caso, es debido a que a pesar de que cada vez son mas asequibles los teléfonos inteligentes que corren Android, personalmente sigo pensando que aún son demasiado caros de adquirir, debido a que las compañías te obligan a contratar carísimas tarifas de voz y datos para poder adquirir un teléfono que funcione con Android. Sin embargo, cada vez hay mas gente que los tiene y por ello demandan nuevas vías de comunicación. De esta forma, podemos aprovechar la situación para ahorrar dinero con estos nuevos canales de comunicación.

En mi caso, tengo una tarifa de voz con tarifa plana que actualmente se encuentra descatalogada y que constantemente mi operadora móvil intenta cambiar con suculentas ofertas para acceder a nuevos terminales con Android. Resulta que ninguna de estas ofertas mejoran mi tarifa actual por muchos caramelos que me enseñe, más bien encarecen el actual consumo. Por no mencionar que además nos dificultan e impiden el uso de la telefonía IP, motivo por el cual sí pondría Internet en el móvil, ya que ésta realmente puede ser un gran ahorro en nuestra factura. Pero lo cierto es que la única forma de hacer telefonía IP por el móvil es a través de una conexión WI-FI o con operadores que tampoco mejoran mi tarifa y además no ofrecen terminales Android.

Todos mis amigos tienen internet en el móvil con aplicaciones de mensajería instantánea. 

¿Cómo puedo ahorrar dinero en mi factura sin la necesidad de disponer de un terminal con Android?

Pongámonos manos a la obra.


Para el sistema operativo Windows, existen numerosos emuladores de Android. Todos ellos son muy buenos porque los he probado. Para mi gusto, el mejor es BlueStacks, aunque esté en versión beta.
Para Mac, no hay disponibles tantos emuladores, y además, el más recomendado es el SDK de Android. El inconveniente de este SDK es que hay que ejecutarlo con eclipse,  y creo que no es lo más apropiado para un usuario que desconoce el mundo de Java. Sinceramente, creo que si no fuese por BlueStacks, que es la otra opción que he encontrado, un usuario cualquiera que desconozca eclipse, Java y todo lo que tenga que ver con programación, no podría ejecutar ninguna aplicación Android en su Mac.

BlueStacks por ahora está en versión alfa para Mac y Beta para Windows, y en un futuro esta aplicación pasará a ser una aplicación de pago, por lo que recomiendo descargar ahora la última versión y guardarla en un pen drive para tener esta aplicación gratis hasta la eternidad.
En la versión de Windows, está bastante bien la actual versión beta, porque nos permite instalar aplicaciones desde el market y además existen páginas donde explican como romper esta versión. Sin embargo en Mac, por ahora, no he encontrado nada que explique como romper la versión de Mac, por lo que en principio, sólo es posible ejecutar las aplicaciones que vienen preinstaladas. Pero esto no es exactamente así, pues en Mac he conseguido instalar aplicaciones nuevas desde el market.

¿Cómo?

Una vez instalado BlueStacks, nos dirigimos al dock .

Seguidamente, seleccionamos la opción "abrir en el Finder". Una vez abierto el finder, copiamos todas las aplicaciones preinstaladas y las pegamos en un lugar seguro.

Volvemos otra vez a la carpeta de aplicaciones Android de nuestro Mac, y seleccionamos una aplicación. Hacemos click con el botón derecho y seguidamente pulsamos sobre la opción “mostrar contenido del paquete”.



El finder nos mostrará una nueva ventana con el contenido del paquete, ya que al parecer, las aplicaciones van comprimidas con una estructura de ficheros y carpetas, donde se definen el paquete de la aplicación, los iconos, etc...


Abrimos la carpeta "Contents" y editamos el fichero "info.plist". Éste es un fichero de texto plano en formato xml, donde se especifica el paquete de la aplicación, versión, plataforma, etc..


Nosotros sólo modificaremos la parte del xml que corresponde con el paquete de la aplicación y su instalador. Para ello, buscaremos en google el nombre del paquete que queremos instalar y lo reemplazaremos en la etiqueta "string" que sigue a la etiqueta "key" cuyo valor es "GuestAppActivity" y "GuestAppPackage" tal y como figura en la siguiente imagen.


En mi caso he puesto la aplicación de gasolineras baratas, porque además de utilizarla bastante, me parece muy útil a la hora de ahorrar dinero en mi coche y también me permite acceder a funcionalidades ocultas que trae el emulador de Android BlueStacks.

Una vez guardado el fichero modificado, cerramos el paquete de la aplicación y la ejecutamos.
Al ejecutarla, BlueStacks intentará arrancar la aplicación que hemos indicado, y al detectar que ésta no está instalada, automáticamente buscará en el "market" de Android el paquete que se reemplazó en el xml y procederá a bajarse la aplicación e instalarla.

El market viene instalado, pero los desarrolladores de BlueStacks no lo han dejado accesible.


Una vez instalada, podemos ejecutar nuestra aplicación desde el escritorio de Android.


Concretamente en la aplicación de gasolineras baratas, existe un espacio reservado a la publicidad. Si pulsamos sobre ella, BlueStacks abrirá un explorardor web, que también han dejado inaccesible por defecto los desarrolladores de BlueStack.


Con el navegador web abierto, podemos acceder a nuestro correo electrónico y bajarnos los contactos de nuestra agenda, para importarlos en aplicaciones como WhatApps o Viber entre otras.


Esta es mi aportación y espero que la disfrutéis.

Si encontráis nuevos trucos para la versión BlueStack de Mac's, me gustaría que la comentaseis en el blog o pusiérais referencias a otros blog's donde podamos aprender nuevos trucos o funcionalidades ocultas.

Muchas gracias.

lunes, 30 de enero de 2012

Cómo hacer para mostrar como alert las excepciones AJAX

A la hora de incluir un UpdatePanel en nuestra página, debemos tener en cuenta que los errores que se produzcan durante las peticiones asíncronas serán capturados por AJAX, por tanto para poder mostrar el error que se está produciendo, deberemos capturar ese error desde JavaScript para mostrarlo como alert.
Para esto debemos incluir en la página que contiene el UpdatePanel, o bien incluyéndolo en un fichero .js, el siguiente código JavaScript:
       
/// 
/// Asociamos el evento de fin de carga AJAX a nuestro
/// método
/// 
function pageLoad() {
     var manager = Sys.WebForms.PageRequestManager.getInstance();
     if (!manager.get_isInAsyncPostBack())
                manager.add_endRequest(endRequest);
}

/// 
/// En esta función controlamos todos los errores que se produzcan
/// a través de AJAX
/// 
function endRequest(sender, args) {
     var error = args.get_error();
     if (error != null) {
         var mensaje = error.description.split(':')[1].trim();
         alert(mensaje);
     }
     args.set_errorHandled(true);
}

martes, 17 de enero de 2012

Guía práctica de cómo saber a quien se llama para ahorrar en nuestra factura telefónica.

Hola muy buenas.

Un buen dia revisando mi factura de telefonía móvil, descubrí que en el listado de detalles de llamadas aparece un prefijo asociado al número de teléfono marcado. Para el caso de los teléfonos fijos, el código de cuatro cifras que precede al número es 0034. Concretamente en mi factura, se omiten los 0 y simplemente me aparece el 34. Recordemos que para los prefijos de teléfonos internacionales, el 34 o +34 corresponde a España. Hasta ahí todo correcto, pero en el mismo listado aparecían los números correspondientes a las llamadas móviles precedidas de un código también de 4 cifras con el formato. XX34, es decir el teléfono marcado era el XX34YYYYYYYYY donde Y corresponde al móvil que hacía mis llamadas.
Pues bien, buscando por internet encuentro que efectivamente el 34 es el código que corresponde a nuestro país, pero los dos primeros dígitos parecían tener una procedencia misteriosa. El misterio se resuelve cuando al repasar las llamadas que aparecen en mi listado y de las cuales sé cuáles son las operadoras que mis contactos tienen por aquello de preguntar cuánto pagan de teléfono, observo que los dos primeros dígitos corresponden a las operadoras móviles que mis contactos están apuntados.

La siguiente lista que he podido recabar es:

7134 Movistar
7234 Vodafone
7334 Orange
7434 Yoigo
4034 Jazztel
4434 Masmovil
7534 Euskaltel
1334 Eroskimovil
5034 Fonyou
2434 Carrefour Móvil
0334 Pepephone

(Esta lista la iremos ampliando más adelante).

Pues bien, desconozco si otras operadoras ofrecen la posibilidad de ver el código de operador tanto de las llamadas realizadas por nuestro número, como las recibidas a nuestro número. Pero si nuestro operador de telefonía móvil nos permitiese ver este dato en alguna de las dos listas de llamadas, podríamos implementar un mecanismo para ahorra en el consumo telefónico.
Por lo pronto se me ocurre clasificar nuestros contactos en grupos telefónicos, es decir, crear un grupo por cada operadora de telefonía móvil y seguidamente añadir nuestros contactos a cada uno de estos grupos. De esta manera tendremos un control sobre el gasto.
Esto es útil para aquellas operadoras, que en función de las llamadas realizadas a otras operadoras, tengan tarifas distintas. Todos sabemos que por ejemplo movistar, cobra más caras las llamadas realizadas a números yoigo. Pues bién, como todos sabemos esto, podemos evitar llamar a los usuarios de yoigo para reducir nuestra factura. No olvidemos que el que se apunta a yoigo sabe que las llamadas realizadas a su número son más caras que el resto. ¿Y por qué?
Hasta ahora pensaba que se trataba de un boicot por parte de movistar a yoigo por alguna razón. El caso es que no es así del todo y paso a explicar porqué.
Resulta que cuando realizamos una llamada desde nuestro número (operador A) a otro número móvil (operador B), el operador A debe pagar un canon al operador B por interconexión. Este canon está establecido por ley.
Pues bien, una vez entendido el concepto de este canon y como se aplica, y quien lleva más a rajatabla el cobro del glide path, podemos establecer una estrategia a la hora de llamar a nuestros contactos.
Por otro lado, gracias a las nuevas tecnologías, podemos aprovechar alguna herramienta existente para revisar constantemente nuestras facturas, revisar nuestro consumo y actualizar nuestra base de datos de contactos para economizar en las llamadas realizadas.
Un ejemplo de ello es una herramienta publicada en la web de la CMT Esta herramienta permite conocer a que operador pertenece una numeración dada
Gracias a los planes de internet móvil, podemos tener esta página en la lista de favoritos de nuestro navegador móvil, y en todo momento podemos consultar el operador al que vamos a realizar la llamada.
Ya será decisión nuestra si llamar o no en función del contrato que tengamos fijado.

En esta entrada podemos tener una relación de aplicaciones android para tal fin.

Un saludo.

Me gustaría que participasen si conocen alguna aplicación móvil que nos facilite el proceso y nos permita ahorrar en nuestro consumo telefónico.