lunes, 9 de marzo de 2015

Dos versiones de la misma aplicación con Android Studio Flavors





Logo Android Studio
Tras un largo tiempo sin publicar nada por falta de tiempo, vengo a hablar de cómo crear dos versiones de la misma aplicación con Android Studio, es algo común a la hora de desarrollar una aplicación el hacer dos versiones de la misma, una gratuita con publicidad y una de pago por ejemplo. Lógicamente no vamos a hacer dos aplicaciones diferentes ya que a la larga el mantenimiento se haría muy complicado.
Para cumplir nuestro cometido utilizaremos la herramienta "Flavors" de Android Studio que está pensada y diseñada para ello, utilizando esta forma de trabajar será el propio Gradle el que se encargue de crear "las dos versiones" de la aplicación, pero nosotros sólo tendremos que mantener una, lo que sea diferente en alguna de las dos versiones se realiza en esa versión y listo.
Antes de Android Studio si utilizabas Eclipse probablemente hayas hecho esto mismo utilizando librerías, es decir, creabas una librería que contenía el código común a las dos versiones y lo que fuese independiente de cada una lo hacías en la versión correspondiente, esto es algo parecido, sólo que simplificado e ideado para hacer el trabajo mucho más fácil.
Aquí veremos una sencilla aplicación de ejemplo que hará la función de calculadora de la cuál crearemos dos versiones diferentes, una que sólo puede sumar y restar (la gratuita) y otra que puede sumar, restar, multiplicar y dividir (la de pago), he de decir que no voy a comentar el código en sí del ejemplo que vemos a ver, comentaré lo que sea relevante para el tema en cuestión, el código propio de la aplicación es tan simple que no requiere comentarios.

Creamos un nuevo proyecto

Empecemos creando un nuevo proyecto al que llamaremos "TestingFlavorsCalc", en el nombre del paquete podemos poner lo que querramos (yo he puesto "com.dolphinziyo.android.testingflavorscalc") y lo demás lo dejamos por defecto.

Creamos los distintos Flavors

Una vez el proyecto ha sido creado y podemos empezar a trabajar lo que haremos será crear los distintos flavors, que llevarán el nombre de "free" y "pro", para ello entramos en las "Preferencias del módulo" presionando F4 o con el botón derecho encima de la zona blanca de la izquierda "Open Módule Settings".
En cuánto se abra, arriba a la izquierda veremos varias pestañas, entre ellas la que dice "Flavors", la abrimos y debajo presionamos en el "+" de color verde. Al hacerlo, el formulario de la derecha cambiará para que introduzcamos los datos específicos de éste, lo que haremos será cambiar el nombre por "free" y lo demás lo copiamos del otro flavor (defaultConfig), el nombre del paquete ("Application Id") debemos cambiarlo para que sea distinto al de la otra versión, si no al subirlo a la Play Store se pensaría que es la misma aplicación, recordemos que Google identifica la aplicación por el nombre del paquete. Yo lo que he hecho ha sido añadir "free" al final del nombre del paquete (quedando así: com.dolphin.android.testingflavorscalcfree).
Y repetimos el proceso anterior para la versión "pro", llamándola "pro" en vez de "free" y añadiéndole la coletilla al nombre del paquete. Le damos a "Apply" para que se apliquen los cambios y luego cerramos el diálogo pulsando en "Ok".

Cambiando entre una versión (Build Variants)

Para poder trabajar en una aplicación u otra es necesario cambiar el Build Variant ya que Android Studio sólo nos permite estar trabajando en único flavor, para ello a la izquierda de Android Studio abajo, vemos una pestaña en vertical que dice "Build Variants", le damos y se nos abre una pequeña ventanita.

Ahí podemos ver dos columnas "Module" y "Build Variant", pues pinchando en la primera línea, columna "Build Variant" (en "freeDebug" en la imagen anterior) se nos abre un menú desplegable que nos permite seleccionar cualquiera de las cuatro (por defecto) versiones de la aplicación, las dos de la gratuita (debug y release) y las dos de la pro (debug y release también).
Como dijimos antes Android Studio sólo permite trabajar con una por tanto si tenemos ahí seleccionado freeDebug sólo podremos trabajar en la versión "free", para hacer cambios en la "pro" tendremos que cambiarlo ahí.

Las carpetas necesarias para los flavors

Android Studio no crea las carpetas que necesitamos para las diferentes versiones ya que él no sabe cuál es el planteamiento que utilizaremos, por tanto tenemos que crearlas a mano, para ello, dentro de la carpeta "src" (dentro de "app"->"java") crearemos dos carpetas que tienen que llamarse igual que el nombre de los flavor para que Gradle puede identificarlos a la hora de compilar, así que le damos botón derecho a "src"->"New"->"Directory" y le llamamos "free" y lo repetimos para la versión "pro".

Como vemos hay cuatro carpetas:
  • androidTest: No la utilizaremos en este ejemplo, almacenaría los tests de la aplicación
  • free: Contendrá las ventanas y recursos independientes de la versión gratuita
  • main: Contiene y contendrá las ventanas y recursos comunes a las dos versiones de la aplicación
  • pro: Contendrá las ventanas y recursos independientes de la versión de pago

El código común a ambas aplicaciones

Para que se haga notorio que las dos aplicaciones son la misma pero con pequeños cambios antes de acceder a la calculadora habrá una ventana con dos botones que nos permitirá acceder a la calculadora o cerrar la aplicación, así que vamos a crearla.
Por comodidad voy a cambiar la forma en que se muestra la estructura del proyecto ya que para trabajar con flavors la vista por defecto no me parece cómoda, así que si tú también quieres hacerlo , arriba a la izquierda dónde pone "Android" pinchamos para abrir el desplegable y seleccionamos "Project".





Primero creamos el "Layout" que contendrá los botones editando el "activity_main" dentro de "main"->"res"->"layout" cuyo código quedaría:
  <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"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  tools:context=".MainActivity">
 
  <Button
  android:id="@+id/btn_entrar_calculadora"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@string/abrir_calculadora"/>
 
  <Button
  android:id="@+id/btn_salir"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@string/salir"/>
</LinearLayout>
Nos mostrará dos errores y es porque no tenemos los strings necesarios, así que vamos a copiar los strings para evitar estos y futuros errores, para ello editamos el fichero "strings.xml" dentro de "main"->"res"->"values" dejándolo así:
  <resources>
    <string name="app_name">TestingFlavorsCalc</string>
    <string name="action_settings">Settings</string>
 
    <string name="num_1">Número 1</string>
    <string name="num_2">Número 2</string>
    <string name="sumar">+</string>
    <string name="restar">-</string>
    <string name="multiplicar">*</string>
    <string name="dividir">/</string>
    <string name="igual">=</string>
    <string name="abrir_calculadora">Calculadora</string>
    <string name="salir">Cerrar aplicacion</string>
  </resources>
Con los errores en el "activity_main.xml" ya resueltos vamos a editar el "MainActivity.java" que contendrá el comportamiento de los botones:
  package com.dolphinziyo.android.testingflavorscalc;
 
  import android.content.Intent;
  import android.support.v7.app.ActionBarActivity;
  import android.os.Bundle;
  import android.view.Menu;
  import android.view.MenuItem;
  import android.view.View;
  import android.widget.Button;
 
 
  public class MainActivity extends ActionBarActivity implements View.OnClickListener {
    Button btnEntrar, btnSalir;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
 
      btnEntrar = (Button) findViewById(R.id.btn_entrar_calculadora);
      btnSalir = (Button) findViewById(R.id.btn_salir);
 
      btnEntrar.setOnClickListener(this);
      btnSalir.setOnClickListener(this);
    }
 
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
      // Inflate the menu; this adds items to the action bar if it is present.
      getMenuInflater().inflate(R.menu.menu_main, menu);
      return true;
    }
 
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
      // Handle action bar item clicks here. The action bar will
      // automatically handle clicks on the Home/Up button, so long
      // as you specify a parent activity in AndroidManifest.xml.
      int id = item.getItemId();
 
      //noinspection SimplifiableIfStatement
      if (id == R.id.action_settings) {
        return true;
      }
 
      return super.onOptionsItemSelected(item);
    }
 
    @Override
    public void onClick(View v) {
      if (v == btnEntrar) {
        startActivity(new Intent(this, VentanaCalculadora.class));
      } else if (v == btnSalir) {
        finish();
      }
    }
  }
Ya tenemos la ventana principal creada, vemos que da un error al final, pero no tenemos que preocuparnos, es porque falta una clase que no hemos creado aun. Antes de ello vamos a crear un objeto que será el que haga la función de la calculadora, y como es común  a las dos versiones a pesar de que una no utilizará todos los métodos, lo crearemos también en la parte general (main), para ello pinchamos con el botón derecho del ratón en el paquete dentro de "main"->"java" y seleccionamos "New"->"Java Class", nos pedirá el nombre, así que ponemos "Calculadora".
Ahora vamos a implementar toda la logística de este objeto que consta de un constructor y cuatro métodos (sumar, restar, dividir y multiplicar):
  package com.dolphinziyo.android.testingflavorscalc;
 
  /**
  * Creado por dolphinziyo el 19/02/2015.
  * http://www.tecnogame.org
  * http://twitter.com/dolphinziyo
  */
 
  public class Calculadora {
    double num1, num2;
 
    Calculadora(double num1, double num2){
      this.num1 = num1;
      this.num2 = num2;
    }
 
    public double sumar(){
      return num1 + num2;
    }
 
    public double restar(){
      return num1 - num2;
    }
 
    public double multiplicar(){
      return num1 * num2;
    }
 
    public double dividir(){
      return num1 / num2;
    }
  }
Ya tenemos creado todo lo común a ambas versiones, así que vamos con el funcionamiento de los flavors pero antes aunque no esté creada la ventana de la calculadora (VentanaCalculadora) vamos a añadir al Manifest el "Activity" de esa ventana, para luego no tener errores por culpa de que no es capaz de encontrarla:
Así quedaría nuestro AndroidManifest:

Códificando los Flavors

  • Versión "Free"

Primero y antes que nada seleccionamos "freeDebug" en el "Build Variants" si no lo tenemos seleccionado ya.
Para esta versión vamos a necesitar un "Layout", un fichero "strings" y un código específico por lo que dentro de la carpeta "free" vamos a crear dos carpetas más, una "java" que contendrá el código y otra "res" (y dentro de esta una llamada "layout") que contendrá los recursos, para ello pulsamos botón derecho en "free"->"New"->"Directory" al que llamamos "java" y de nuevo botón derecho en "free"->"New"->"Android resource directory", en la ventana que se nos abre nos pide varias cosas, de "Directory Name" ponemos "layout" en "Resource Type" seleccionamos también "layout" y en "Source Set" seleccionamos "free", le damos a "Ok" y ya nos crea la carpeta "res" que contiene dentro la de los "layout".

Del modo que hicimos con la carpeta "layout" hacemos lo mismo para crear la de "values" ya que llevará algunos textos específicos, para ello botón derecho encima de "res"->"New"->"Android resource directory", y ponemos "values" en "Directory Name", seleccionamos "values" en el desplegable de "Resource Type" y "free" en el de "Source Set".
Ahora creamos el layout principal que llevará nuestra ventana en la carpeta "layout" creada antes en este mismo paso, al layout le llamaremos "calc" para ello botón derecho en la carpeta y "New"->"XML"->"Layout XML File":
  <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"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin">
 
  <TextView
  android:text="@string/gratuita"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_marginTop="10dp"
  android:layout_marginBottom="10dp"
  android:textSize="20sp"
  android:gravity="center"/>
 
  <LinearLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="horizontal">
 
  <EditText
  android:id="@+id/et_num1"
  android:layout_width="0dip"
  android:layout_height="wrap_content"
  android:layout_weight="1"
  android:hint="@string/num_1"
  android:inputType="numberDecimal"
  android:gravity="center"/>
 
  <TextView
  android:id="@+id/tv_operacion"
  android:layout_width="0dip"
  android:layout_height="wrap_content"
  android:layout_weight="0.10"
  android:gravity="center"/>
 
  <EditText
  android:id="@+id/et_num2"
  android:layout_width="0dip"
  android:layout_height="wrap_content"
  android:layout_weight="1"
  android:hint="@string/num_2"
  android:inputType="numberDecimal"
  android:gravity="center"/>
 
  <TextView
  android:layout_width="0dip"
  android:layout_height="wrap_content"
  android:layout_weight="0.10"
  android:text="@string/igual"
  android:gravity="center"/>
 
  <TextView
  android:id="@+id/tv_resultado"
  android:layout_width="0dip"
  android:layout_weight="1"
  android:layout_height="wrap_content"
  android:gravity="center" />
</LinearLayout>
 
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
 
<Button
android:id="@+id/btn_sumar"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/sumar" />
 
<Button
android:id="@+id/btn_restar"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/restar" />
</LinearLayout>
</LinearLayout>
Si por casualidad muestra error en alguna de las siguientes líneas, cambiar momentáneamente al "Build Variant" "proDebug" u otro cualquiera y volver al "freeDebug":
xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
Vamos a crear también el fichero strings específico de la versión gratuita, para ello hacemos igual que con el layout "calc" sólo que esta vez pulsando encima de "values" y seleccionando "Values XML File", como nombre ponemos "strings", dentro contiene:

  <?xml version="1.0" encoding="utf-8"?>
  <resources>
    <string name="app_name">TestingFlavorsCalc Free</string>
    <string name="gratuita">Versión gratuita</string>
  </resources>
Sólo nos falta añadir el comportamiento de la ventana de la calculadora gratuita, así que dentro de la carpeta "free"->"java" creamos el paquete antes que nada, botón derecho "New"->"Package" y lo llamamos igual que el nombre del paquete principal (en este ejemplo "com.dolphinziyo.android.testingflavorscalc") y dentro del paquete creamos un nuevo "Java Class" que llamaremos "VentanaCalculadora", una vez creado sustituimos el código por el siguiente:

  package com.dolphinziyo.android.testingflavorscalc;
 
  import android.app.Activity;
  import android.os.Bundle;
  import android.view.View;
  import android.widget.Button;
  import android.widget.EditText;
  import android.widget.TextView;
 
  public class VentanaCalculadora extends Activity implements View.OnClickListener{
    Button btnSumar, btnRestar;
    EditText etNum1, etNum2;
    TextView tvResultado, tvOperacion;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.calc);
 
      btnSumar = (Button)findViewById(R.id.btn_sumar);
      btnRestar = (Button)findViewById(R.id.btn_restar);
 
      btnSumar.setOnClickListener(this);
      btnRestar.setOnClickListener(this);
 
      etNum1 = (EditText)findViewById(R.id.et_num1);
      etNum2 = (EditText)findViewById(R.id.et_num2);
      tvResultado = (TextView)findViewById(R.id.tv_resultado);
      tvOperacion = (TextView)findViewById(R.id.tv_operacion);
    }
 
    @Override
    public void onClick(View v) {
      double num1, num2;
      if(etNum1 != null && etNum2 != null){
        if(!etNum1.getText().toString().equals("")){
          num1 = Float.parseFloat(etNum1.getText().toString());
        }else{
          num1 = 0;
        }
        if(!etNum2.getText().toString().equals("")){
          num2 = Float.parseFloat(etNum2.getText().toString());
        }else{
          num2 = 0;
        }
      }else{
        num1 = 0;
        num2 = 0;
      }
      Calculadora calc = new Calculadora(num1, num2);
      if(v == btnSumar){
        tvResultado.setText("" + calc.sumar());
        tvOperacion.setText(getResources().getString(R.string.sumar));
      }else if(v == btnRestar){
        tvResultado.setText("" + calc.restar());
        tvOperacion.setText(getResources().getString(R.string.restar));
      }
    }
  }
Llegados a este punto ya podemos ejecutar la aplicación, la versión gratuita ya está hecha y completamente funcional, pero nos falta la versión "pro", así que…
  • Versión "pro"

Para no alargar más el artículo simplemente dejaré aquí los códigos de la versión "pro", ya que lo demás es igual que la versión "free", no olvides cambiar el "Build Variant" al "proDebug".
Tras crear las carpetas necesarias y el paquete para el código, les asignamos su comportamiento:
  • strings.xml:
  <?xml version="1.0" encoding="utf-8"?>
  <resources>
    <string name="de_pago">Versión de pago</string>
  </resources>
  • calc.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"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin">
 
  <TextView
  android:text="@string/de_pago"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_marginTop="10dp"
  android:layout_marginBottom="10dp"
  android:textSize="20sp"
  android:gravity="center"/>
 
  <LinearLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="horizontal">
 
  <EditText
  android:id="@+id/et_num1"
  android:layout_width="0dip"
  android:layout_height="wrap_content"
  android:layout_weight="1"
  android:hint="@string/num_1"
  android:inputType="numberDecimal"
  android:gravity="center"/>
 
  <TextView
  android:id="@+id/tv_operacion"
  android:layout_width="0dip"
  android:layout_height="wrap_content"
  android:layout_weight="0.10"
  android:gravity="center"/>
 
  <EditText
  android:id="@+id/et_num2"
  android:layout_width="0dip"
  android:layout_height="wrap_content"
  android:layout_weight="1"
  android:hint="@string/num_2"
  android:inputType="numberDecimal"
  android:gravity="center"/>
 
  <TextView
  android:layout_width="0dip"
  android:layout_height="wrap_content"
  android:layout_weight="0.10"
  android:gravity="center"
  android:text="@string/igual" />
 
  <TextView
  android:id="@+id/tv_resultado"
  android:layout_width="0dip"
  android:layout_weight="1"
  android:layout_height="wrap_content"
  android:gravity="center" />
</LinearLayout>
 
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
 
<Button
android:id="@+id/btn_sumar"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/sumar" />
 
<Button
android:id="@+id/btn_restar"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/restar" />
 
<Button
android:id="@+id/btn_multiplicar"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/multiplicar" />
 
<Button
android:id="@+id/btn_dividir"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/dividir" />
</LinearLayout>
</LinearLayout>
  • VentanaCalculadora.java:
  package com.dolphinziyo.android.testingflavorscalc;
 
  import android.app.Activity;
  import android.os.Bundle;
  import android.view.View;
  import android.widget.Button;
  import android.widget.EditText;
  import android.widget.TextView;
 
  public class VentanaCalculadora extends Activity implements View.OnClickListener{
    Button btnSumar, btnRestar, btnMultiplicar, btnDividir;
    EditText etNum1, etNum2;
    TextView tvResultado, tvOperacion;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.calc);
 
      btnSumar = (Button)findViewById(R.id.btn_sumar);
      btnRestar = (Button)findViewById(R.id.btn_restar);
 
      if(BuildConfig.FLAVOR.equals("pro")){
        btnMultiplicar = (Button)findViewById(R.id.btn_multiplicar);
        btnDividir = (Button)findViewById(R.id.btn_dividir);
 
        btnMultiplicar.setOnClickListener(this);
        btnDividir.setOnClickListener(this);
      }
 
      btnSumar.setOnClickListener(this);
      btnRestar.setOnClickListener(this);
 
      etNum1 = (EditText)findViewById(R.id.et_num1);
      etNum2 = (EditText)findViewById(R.id.et_num2);
      tvResultado = (TextView)findViewById(R.id.tv_resultado);
      tvOperacion = (TextView)findViewById(R.id.tv_operacion);
    }
 
    @Override
    public void onClick(View v) {
      double num1, num2;
      if(etNum1 != null && etNum2 != null){
        if(!etNum1.getText().toString().equals("")){
          num1 = Float.parseFloat(etNum1.getText().toString());
        }else{
          num1 = 0;
        }
        if(!etNum2.getText().toString().equals("")){
          num2 = Float.parseFloat(etNum2.getText().toString());
        }else{
          num2 = 0;
        }
      }else{
        num1 = 0;
        num2 = 0;
      }
      Calculadora calc = new Calculadora(num1, num2);
      if(v == btnSumar){
        tvResultado.setText("" + calc.sumar());
        tvOperacion.setText(getResources().getString(R.string.sumar));
      }else if(v == btnRestar){
        tvResultado.setText("" + calc.restar());
        tvOperacion.setText(getResources().getString(R.string.restar));
      }else if(v == btnMultiplicar){
        tvResultado.setText("" + calc.multiplicar());
        tvOperacion.setText(getResources().getString(R.string.multiplicar));
      }else if(v == btnDividir){
        tvResultado.setText("" + calc.dividir());
        tvOperacion.setText(getResources().getString(R.string.dividir));
      }
    }
  }
Este es el resultado:
VERSIÓN FREEVERSIÓN PRO


Puedes descargar el proyecto con todo preparado y listo para ejecutar de mi Github
Y eso es todo, cambiando el "Build Variant" puedes ejecutar una versión de la aplicación u otra. Al generar la aplicación firmada se exportarán las dos versiones listas para publicar en Play Store, espero que este artículo haya sido útil y como siempre cualquier duda, sugerencia o comentario puedes dejarlo abajo.
Relacionado:

Problemas con las imagenes en angular. %7B%7B %7D%7D

Si desarrollamos una aplicación con angular e intentamos insertar dinámicamente imágenes en una etiqueta <img src="{{ ruta_dinamica_de_la_imagen }}" podemos encontrarnos errores de redsimilar a la imagen de abajo. (Developer Tool de chrome)

Estos errores producen demoras de tiempo en nuestras peticiones, ralentizando nuestras aplicaciones. Esto se debe a que la etiqueta src no soporta los caracteres {{ }} que necesitamos para definir la variable de angular. La representacion hexadecimal de los caracteres {{ }} se traducen en %7B%7B %7D%7D produciendo el error en cuestión. 
Solución: Para solucionarlo, basta con cambiar la etiqueta <img src .. por <img ng-src .. esta directiva de angular evitará nuestro problema. Mas abajo se completa la información con documentación oficial extraida de Angular Js. 
Nota: análogamente podemos encontrar el problema con la etiqueta HREF y la solución es usar la directiva <a ng-href="{{ ruta_dinamica_angular }}"



Información extraida de la documentacion oficial de angular JS,

https://docs.angularjs.org/api/ng/directive/ngSrc



ngSrc

  1. - directive in module ng

Using Angular markup like {{hash}} in a src attribute doesn't work right: The browser will fetch from the URL with the literal text {{hash}} until Angular replaces the expression inside {{hash}}. The ngSrcdirective solves this problem.
The buggy way to write it:
<img src="http://www.gravatar.com/avatar/{{hash}}"/>
The correct way to write it:
<img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>

Directive Info

  • This directive executes at priority level 99.

Usage

  • as attribute:
    <IMG
      ng-src="">
    ...
    </IMG>


Arguments

ParamTypeDetails
ngSrctemplate
any string which can contain {{}} markup.

jueves, 5 de marzo de 2015

tabla ascii

muy útil cuando el teclado da problemas y no podemos escribir algún caracter.
En windows pulsar la tecla ALT + <CODIGO_DECIMAL_ASCII_CON_TECLADO_NUMERICO> y soltar una vez introdudico el codigo completo. El caracter debe aparecer.


miércoles, 18 de febrero de 2015

Desarrollar app en dispositivo apple: iphone, ipad, ipod... y probar a compilar nuestra app en el dispositivo

Tengo realizada una app para iphone con xCode que funciona perfectamente en mi simulador. El problema viene cuando quería realizar el test sobre mi iphone.  Despues de estar horas intentandolo. La conclusión es: 

 1.- Si tiene tu telefono con jailbreak. Puedes compilarla e instalarla sin mayor problemas
 2.- Eres un tío legal. No te gustan los líos y tu telefono está con el sistema sin modificar. Aquí tienes un problema. Te pasa por legal, :p. 


Este post me fue de gran ayuda para confirmar mi problema: 


Miquel Camps Orteza 1533 | 11/07/2013 18:42
Este post viene a raíz de perder una hora intentando comprender porqué no podía instalar una app en un iPhone.

Para instalar una app en desarrollo en un dispositivo iOS necesitas:

1- Tener una licencia de desarrollador iOS de apple (99$)
Nota: si quieres probar tu app en distintos dispositivos no hace falta que cada uno tenga una licencia de desarrollador, una cuenta puede dar de alta hasta 100 dispositivos.

2- Dar permiso a tu dispositivo para que pueda ejecutar apps en desarrollo:
Xcode > Organizer > Devices > Seleccionas el dispositivo > Use for development

3- En el member center dar de alta los dispositivos con el UID.

4- Crear un certificado provisional de desarrollo con permisos a los dispositivos que quieres para testear la app.

5- Enchufar el dispositivo a un ordenador con Xcode y el certificado provisional y hacer click en el botón Run.


----------------------------------------------------------------------------------------------------------

En el sitio de apple explican unas breves nociones para iniciarse en la programación de dispositivos apple: 


Y el enlace directo a getting started: 



Si obtas por obtener la licencia de desarrollador. Este post es muy útil para crear el certificado provisional de desarrollo que necesitarán nuestras apps. 

Nota: Este certificado viene generar una identificador único para identificar nuestra app. Apple lo llama Bundle ID.


Origen: 

xcode

PASO A PASO: SOLICITUD DE CERTIFICADOS PARA APLICACIONES IOS (APPLE – APPSTORE) [ACTUALIZADO 09/04/13]

DESPUÉS DE DESARROLLAR MI PRIMERA APLICACIÓN PARA IPHONE Y PROBARLA EN EL SIMULADOR INCLUIDO EN XCODE, ERA EL MOMENTO DE DARME DE ALTA EL PROGRAMA DE DESARROLLADORES DE APPLE PARA PROBAR LA APLICACIÓN EN MI PROPIO IPHONE, PERO EL QUEBRADERO DE CABEZA FUE GRANDE, AQUÍ DEJO LO QUE INTENTA SER UN MANUAL PASO A PASO DE LO QUE HAY QUE HACER PARA CONSEGUIRLO.

[Actualización 9 de Abril de 2013 – Apple acaba de informar de un cambio el Provisioning Portal: este portal ha dejado de existir como tal y han creado una web donde se puede realizar todo lo dicho, siguiendo el mismo procedimiento, pero en otras pantallas. Para acceder a esta nueva web es necesario acceder al portal de desarrollador y seguir el enlace “Prepare for App Submission” ]
Es importante comentar primero que he podido comprobar que desde que se empezó a desarrollar para iOS el procedimiento para darse de alta en el programa de desarrolladores ha variado en algunos aspectos mucho y en otros un poco menos, pero de cualquier forma, siempre está sujeto a futuros cambios.
En mi caso particular, si no lo sabes ya porque estás en un paso anterior, es que para desarrollar aplicaciones para iPhone/iPad/iPod  Touch o cualquier otro dispositivo de Apple que lleve iOS son necesarias dos cosas:
  • Un ordenador de Apple, ya sea un iMac o un MacBook
  • Inscribirse en el iOS Developer Center, donde se puede descargar XCode y todo el material necesario
Con estos dos elementos ya podemos hacer nuestra aplicación y probarla con el Simulador de dispositivos.

ENTONCES, LLEGÓ LA BUROCRACIA…

Una vez que tenemos nuestra aplicación desarrollada y probada con el Simulador ya podemos darnos de alta en el iOS Developer Program (antes sí que se puede pero no es necesario) para poder tener nuestra licencia oficial de desarrollador de Apple:
  1. Registrarse como desarrollador en el iOS Developer Program, cuyo precio es de 99 dólares al año y que permite subir todas las aplicaciones que se quieran y adicionalmente tendremos acceso al iTunes Connect para gestionar nuestra cuenta de desarrollador y nuestras aplicaciones. Es importante decir que cuando no se renueva la licencia del programa de desarrollador las aplicaciones desaparecerán del AppStore y no podrán ser descargadas.
  2. Creación de la solicitud del certificado. (i) Primero en nuestro ordenador debemos ir a la aplicación ‘Llaveros’ o ‘Acceso a Llaveros’ que se encuentra dentro de la carpeta ‘Otros’ junto con las aplicaciones. (ii) Ir al “menú de la aplicación > Preferencias > Certificados” y marcar como Desactivado el OCSP y el CRL. (iii) Ir al “menu de la aplicación > Asistente para Certificados > Solicitar un certificado de una autoridad de certificación” donde se abrirá el dicho asistente y le indicaremos los datos y marcaremos la opción “Guardar en disco” y al pulsar siguiente solicitará la ubicación del fichero “CertificateSigningRequest.certSigningRequest” que debemos recordar.CreacionSolicitud
  3. Creación del certificado. Desde el centro de desarrolladores, accediendo al iOS Provisioning Portal se debe solicitar la creación de nuestro certificado. Para ello se pulsa el botón “Request Certificate” y elegimos el fichero que hemos creado en el paso anterior. Una vez subido el fichero se debe esperar un tiempo, que puede llegar hasta 24 horas (personalmente tardo 2 minutos).
  4. Descarga e Instalación del certificado. Desde el propio iOS Provisioning Portal podremos descargar nuestro certificado ya generado, pulsando en el botón “Download”. Una vez descargado se instala haciendo doble clic en el fichero. Podremos comprobar en la aplicación ‘Llaveros’ que ya aparece un certificado que dice “iPhone Developer XXX” y “iPhone Distribution XXX”. En mi caso solamente lo he realizado para distribución y el de desarrollo se ha hecho automáticamente, si no fuera así, habría que realizar el mismo procedimiento pero en diferente pestaña dentro del iOS Provisioning Portal.CertificadoiOS
  5. Registro de dispositivos. Desde el iOS Provisioning Portal, accedemos al apartado “Devices” donde pulsando el botón “Add Device” podremos agregar el terminal. Para ello, necesitaremos el ID del mismo que se obtiene de dos formas, habiendo conectado previamente el dispositivo al ordenador: (A) desde XCode accediendo al menú Windows > Organizer y seleccionando el dispositivo aparece el ID o (B) desde iTunes pulsamos en la parte izquierda en el terminal y aparece la ventana resumen del mismo, donde aparece el IMEI se puede pulsar y aparece el ID que podremos copiar y posteriormente pegar en la web del iOS Provisioning Portal. Una vez hecho esto, desde XCode (versión 4.5.2) yendo al organizador y pulsando en el dispositivo, pulsaremos “Use for Development” y automáticamente empezará un proceso contra las web de Apple de autenticación del terminal e instalando los perfiles necesarios en el teléfono.
Desde este momento se podrán probar las aplicaciones en el terminal directamente, eligiendo el destino cuando pulsamos “Run” en el entorno de desarrollo: en vez de elegir Simulador iPhone, podremos elegir iPhone de XXX.




jueves, 22 de enero de 2015

Javascript: Como ordenar un array de objetos por cualquier campo

Javascript: Como ordenar un array de objetos por cualquier campo


Ordenar descendentemente por value
items = [ {id:1, value:3, perc:0.5}, {id:2, value:2, perc:0.3}, {id:3, value:1, perc:0.2} ]
items.sort(function (a, b){
    return (b.value - a.value)
})
Ordenar ascendentemente por perc
items.sort(function (a, b) {
    return (a.perc - b.perc)
})

Fuente: https://comoprogramo.wordpress.com/2012/06/29/como-ordenar-en-javascript-un-array-de-objetos-por-cualquier-campo/

Jesús Moreno - Ingeniero Ténico Informático - consultor Informático

Hola, soy Jesús Moreno Ingeniero Técnico Informático en sistemas por la US y propietario de éste blog. Mi trabajo en los ultimos años se ...