Deshabilitar multitarea en Apps iOS 4

Posted on mayo 25th, 2012 in Desarrollo iPhone | 156 Comments »

En ocasiones, puede ser interesante que la aplicación no se mantenga en memoria cuando el usuario presiona el botón Home del dispositivo. Por defecto, cuando el usuario vuelva a entrar, aparecerá en la misma vista en la que estaba cuando presionó el botón.

Esto está bien, pero en determinado tipo de aplicaciones, quizás nos interesa más sacar la aplicación de memoria que gestionar en cada vista el estado, de modo que, cuando volvamos, podamos mostrar al usuario aquello que ya tenía.

Para conseguir este comportamiento en nuestra App, basta con editar el fichero de propiedades (Info.plist) y añadir la siguiente propiedad con el valor “YES”:

"Application does not run in background"

Actualización:

Existe otra opción, aunque probablemente no será la recomendad por Apple. En nuestro AppDelegate, podemos forzar el cierro de la App cuando esta pase al modo background:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    exit(0);
}

Configurar ADB para usar Nexus S en linux

Posted on mayo 25th, 2012 in Desarrollo Android | 142 Comments »

Si, una vez conectado el teléfono por USB, este no es detectado, deberemos configurar udev tal que así:

1. Crear el fichero /etc/udev/rules.d/51-android.rules.

2. Añadir el siguiente contenido (como dice el título, esto es válido para el Nexus S):

 #      NEXUS S
 SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e21",
 SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e22", SYMLINK+="android_adb"
 SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e20", SYMLINK+="android_fastboot"

3. Ahora, en la línea de comandos, lanzamos lo siguiente:

 sudo chmod a+r /etc/udev/rules.d/51-android.rules

4. Reiniciamos y ya deberiamos ver nuestro dispositivo lanzando “adb devices” en la línea de comandos.

5. Una vez lanzado deberiamos ver algo tal que así

 List of devices attached
 3130F74D00E600EC	device

6. Si obtuviéramos una respuesta del siguiente tipo:

 List of devices attached
 ???????????? no permissions

Tendremos que lanzar adb con permisos de root:

 $ ./adb kill-server
 $ sudo ./adb start-server
 $ ./adb devices

En caso de tener que instalar otros dispositivos diferentes al Nexus S, puedes encontrar la configuración en http://developer.android.com/guide/developing/device.html.

Cómo añadir Mobfox usando Admob como backfill

Posted on febrero 24th, 2012 in Desarrollo Android | 157 Comments »

Aquí se describe cómo añadir Mobfox a una aplicación Android que, a su vez, usará Admob como backfill.

Paso 1:

Añadir los SDK de Admob y Mobfox al proyecto poniendo los respectivos jars en la carpeta “libs”.

Paso 2:

Editar el fichero AndroidManifest.xml y añadir lo siguiente:

<activity android:name="com.mobfox.sdk.InAppWebView"/>
<activity android:name="com.google.ads.AdActivity"
          android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
          android:configChanges="orientation|keyboard|keyboardHidden"
         />


Paso 3:

Añadir la siguiente vista en el layout correspondiente de main.xml:

<RelativeLayout
   android:id="@+id/mobfoxContent"
   android:layout_width="fill_parent"
   android:layout_height="50dp"
   android:layout_centerHorizontal="true"
   android:layout_alignParentBottom="true" >
</RelativeLayout>


Paso 4:

Añadir los siguiente imports en la Activity:

Import com.google.ads.Ad;
Import com.google.ads.AdListener;
Import com.google.ads.AdRequest;
Import com.google.ads.AdSize;
Import com.google.ads.AdView;
Import com.google.ads.AdRequest.ErrorCode;
Import com.mobfox.sdk.BannerListener;
Import com.mobfox.sdk.MobFoxView;
Import com.mobfox.sdk.Mode;
Import com.mobfox.sdk.RequestException;


Paso 5:

Añadir las siguientes declaraciones:

private final static String ADMOB_PUBLISHER_ID = "";
private final static String MOBFOX_PUBLISHER_ID = "";
private ViewFlipper viewFlipper;
private MobFoxView mobfoxView;
private AdView adMobView;
private AdRequest adMobRequest;
private final static int REFRESH_AD = 101;
private final static long REFRESH_INTERVAL = 30000;
private Handler refreshHandler;
private Looper refreshLooper;
private RelativeLayout layout;


Paso 6:

Añadir lo siguiente al método onCreate():

boolean includeLocation = false;
boolean useAnimation = true;

mobfoxView = new MobFoxView(this, MOBFOX_PUBLISHER_ID, Mode.LIVE, includeLocation, useAnimation);
mobfoxView.setBannerListener(this);

adMobView = new AdView(this, AdSize.BANNER, ADMOB_PUBLISHER_ID);
adMobView.setAdListener(this);
adMobRequest = buildAdMobRequest();

viewFlipper = new ViewFlipper(this) {
    @Override
    protected void onDetachedFromWindow() {
        try {
            super.onDetachedFromWindow();
        }
        catch (IllegalArgumentException e) {
            stopFlipping();
        }
    }
};

viewFlipper.addView(mobfoxView);
viewFlipper.addView(adMobView);

if (useAnimation)
{
    Animation fadeInAnimation =
        new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f,
        Animation.RELATIVE_TO_PARENT, +1.0f, Animation.RELATIVE_TO_PARENT, 0.0f);
    fadeInAnimation.setDuration(1000);
    Animation fadeOutAnimation =
        new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f,
        Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, -1.0f);
    fadeOutAnimation.setDuration(1000);
    viewFlipper.setInAnimation(fadeInAnimation);
    viewFlipper.setOutAnimation(fadeOutAnimation);
    viewFlipper.setAnimateFirstView(true);
}

//Show MobFoxView viewFlipper.setDisplayedChild(0);
layout = (RelativeLayout) findViewById(R.id.mobfoxContent);
layout.addView(viewFlipper);
Thread refreshThread = new Thread(){
    @Override
    public void run() {
        Looper.prepare();
        refreshLooper=Looper.myLooper();
        refreshHandler = new Handler(refreshLooper) {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case REFRESH_AD:
                    {
                        mobfoxView.loadNextAd();
                        break;
                    }
                }
            }
        };
        Looper.loop();
    }
};
refreshThread.start();


Paso 7:

Añadir los siguientes métodos a la Activity

public AdRequest buildAdMobRequest()
{
    AdRequest adMobRequest =
    new AdRequest();
    //Add targeting info if you like
    //adMobRequest.setGender(Gender.FEMALE);
    //adMobRequest.setBirthday(birthDate);
    //adMobRequest.setLocation(location);
    adMobRequest.addKeyword("game");
    adMobRequest.setTesting(false);
    return adMobRequest;
}

@Override
protected void onPause() {
    super.onPause();
    if (refreshHandler != null)
    {
        refreshHandler.removeMessages(REFRESH_AD);
    }
    if (adMobView!=null)
    {
        adMobView.stopLoading();
    }
    mobfoxView.pause();
}

@Override
protected void onResume() {
    super.onResume();
    if (refreshHandler != null)
    {
        refreshHandler.removeMessages(REFRESH_AD);
        refreshHandler.sendEmptyMessage(REFRESH_AD);
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (refreshLooper != null)
    {
        refreshLooper.quit();
        refreshLooper = null;
        refreshHandler = null;
    }
}

@Override
public void bannerLoadSucceeded() {
    this.runOnUiThread(new Runnable() {
        public void run() {
            if (viewFlipper.getCurrentView()!=mobfoxView)
            {
                viewFlipper.setDisplayedChild(0);
            }
        }
    });
}

//Mobfox events
@Override
public void noAdFound() {
    this.runOnUiThread(new Runnable() {
        public void run() {
        }
    });
    try
    {
        adMobView.loadAd(adMobRequest);
    }
    catch (Exception ex)
    {
        if (refreshHandler!=null)
        {
            refreshHandler.sendEmptyMessageDelayed(REFRESH_AD, REFRESH_INTERVAL);
        }
    }
}

@Override
public void bannerLoadFailed(RequestException e) {
    this.runOnUiThread(new Runnable() {
        public void run() {
        }
    });
    try
    {
        adMobView.loadAd(adMobRequest);
    }
    catch (Exception ex)
    {
        if (refreshHandler!=null)
        {
            refreshHandler.sendEmptyMessageDelayed(REFRESH_AD, REFRESH_INTERVAL);
        }
    }
}

//Admob events
@Override
public void onReceiveAd(Ad arg0) {
    this.runOnUiThread(new Runnable() {
        public void run() {
            if (viewFlipper.getCurrentView()!=adMobView)
            {
                viewFlipper.setDisplayedChild(1);
            }
        }
    });
    if (refreshHandler!=null)
    {
        refreshHandler.sendEmptyMessageDelayed(REFRESH_AD, REFRESH_INTERVAL);
    }
}

@Override
public void onFailedToReceiveAd(Ad arg0, ErrorCode arg1) {
    this.runOnUiThread(new Runnable() {
        public void run() {
        }
    });
    if (refreshHandler!=null)
    {
        refreshHandler.sendEmptyMessageDelayed(REFRESH_AD, REFRESH_INTERVAL);
    }
}

@Override
public void onDismissScreen(Ad arg0) {
    // TODO Auto-generated method stub
}
@Override
public void onLeaveApplication(Ad arg0) {
    // TODO Auto-generated method stub
}
@Override
public void onPresentScreen(Ad arg0) {
    // TODO Auto-generated method stub
}


Paso 8:

La Activity debe implementar BannerListener y AdListener, con lo que quedaría algo parecido a esto:

public class MyAndroidApplication extends Activity implements BannerListener, AdListener {


Paso 9:

Añadir los IDs de publisher correspondientes:

private final static String ADMOB_PUBLISHER_ID = "";
private final static String MOBFOX_PUBLISHER_ID = "";

¡Hecho!

Ahora tu aplicación ya recibe anuncios de Mobfox usando Admob como backfill.

Untar ficheros tar, .tar.gz y tar.bz2

Posted on noviembre 28th, 2011 in Linux | 174 Comments »

Los ficheros tar pueden venir comprimidos o sin comprimir. Si están comprimidos, normalmente será con gzip o bzip2. El programa tar, puede abrir cualquiera de estos tipos.

Comandos a usar:

tar xvzf file-1.0.tar.gz – para descomprimir un fichero tar con gzip (.tgz or .tar.gz)
tar xvjf file-1.0.tar.bz2 – para descomprimir un fichero tar con bzip2 (.tbz or .tar.bz2)
tar xvf file-1.0.tar – para descomprimir un fichero tar (.tar)

Descripción de los parámetros:

x = eXtract, indica extracción
v = verbose (opcional) muestra los archivos procesados
z = gzip; j = bzip2
f = from/to file … (después de la f se pone el fichero que se usará)

Los ficheros serán extraídos en la carpeta actual.

URL de portlet en theme

Posted on octubre 10th, 2011 in Liferay | 142 Comments »

Podemos añadir cualquier portlet en un fichero de un theme de la siguiente manera.

Creamos la url del portlet usando el objeto portletURLFactory, al que le pasamos el siguiente valor, dependiendo de nuestras necesidades:

#*
  P_ID = El ID del portlet.
  PHASE = RENDER_PHASE/ACTION_PHASE
*#
#set ($portlet_url = $portletURLFactory.create($request, "P_ID", $page.getPlid(), "PHASE"))

Una vez tenemos esto, añadimos el estado de la ventana, el modo y los parámetros:

$portlet_url.setWindowState("maximized")
$portlet_url.setPortletMode("view")
$portlet_url.setParameter("struts_action", "message/edit")

Y, por último, usamos la url:

<a href="$portlet_url.toString()">link a mi portlet</a>

Enjoy adding portlet url in liferay theme files !

Habilitar la opción de mover una App Android a la tarjeta SD

Posted on septiembre 22nd, 2011 in Desarrollo Android | 139 Comments »

Esta opción es interesante y, cada vez más, hay más gente que pide que se incluya en las aplicaciones.

Aunque la opción fue añadida en la versión 2.2, es posible incluirla en versiones previas de la SDK. Incluso una App hecha con la SDK 1.5 puede incluir esta función, aunque eso sí, la opción de mover a la SD sólo estará disponible en aquellos móviles con versión 2.2 o posterior.

Vamos con los pasos a seguir:

1. Modificar el Manifest

Edita el fichero AndroidManifest.xml file de la aplicación y añade el atributo “android:installLocation” en la etiqueta :

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.yourapp"
      android:versionName="1.0.1" android:versionCode="3"
      android:installLocation="auto">

En este caso ponemos “auto”, lo que dará al usuario la opción de instalar la App en la memoria del móvil o en la SD. Otros valores son “internalOnly”, que es la opción por defecto si no se añade esta configuración, y “preferExternal”, que instalará la aplicación en la SD primero, si hay espacio para ello.

2. Actualizar el Build Target.

Como ya he mencionado, esto funciona en versiones anteriores a la 2.2, con lo que dejaremos la propiedad “minSdkVersion” tal cual la tenemos. Sin embargo, el “Build target” si debemos modificarlo a la versión 8 (SDK 2.2). En caso contrario, recibiríamos el siguiente error:

error: No resource identifier found for attribute ‘installLocation’ in package ‘android’


3. Cargar la App en el simulador con SDK 2.2

Esto es todo. Si vamos al móvil o simulador, en la administración de aplicaciones, veremos que tenemos disponible el botón de mover al SD.

Acceder a valores del portal desde un portlet

Posted on septiembre 20th, 2011 in Liferay | 171 Comments »

Estos días, estoy trabajando en un portlet que, por diversas razones, necesita saber en qué página aparece del portal. Aunque parezca una estupidez, he pasado bastante tiempo mirando cómo obtener esta información, ya que parece, esto no está contemplado por el estandar de construcción de Portlet.

Finalmente, he logrado la respuesta para Liferay y, de nuevo, era mucho más simple de lo que parecía, por eso lo de estúpido. De todos modos, ahí va, por si alguien se puede evitar perder tiempo con algo como esto.

En el request tenemos un atributo que contiene el objeto ThemeDisplay. Este lo podemos obtener de la siguiente manera:

ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute (
     com.liferay.portal.kernel.util.WebKeys.THEME_DISPLAY);


Si lo usamos dentro de un jsp, no es necesario obtenerlo, ya que Liferay lo hace por nosotros, con lo que sólo tendremos que acceder al objeto themeDisplay:

<%
    long currentGroup = themeDisplay.getParentGroupId();
    long currentLayout = themeDisplay.getLayout().getLayoutId();
%>


Variables personalizadas en Velocity

Posted on septiembre 15th, 2011 in Liferay | 131 Comments »

A veces nos encontramos creando o modificando un Theme de Liferay y necesitamos usar nuestras propias variables que, por supuesto, no vienen definidas en Liferay.

Para ello, debemos usar un hook. En él, crearemos una clase que extenderá com.liferay.portal.kernel.events.Action y en la que sobreescribiremos el método run(). Será en este donde añadamos nuestras variables.

public void run(final HttpServletRequest req,
        final HttpServletResponse res) throws ActionException {

    Map<Object, Object> vmVariables = new HashMap<Object, Object>();
    vmVariables.put("mimensaje", "este mensaje podría ser recuperado en el theme");
    req.setAttribute(WebKeys.VM_VARIABLES, vmVariables);
}

Una vez tenemos la clase, sólo tenemos que declararla en el fichero portal.properties del hook:

servlet.service.events.pre=com.ejemplo.CustomVelocityVariablesAction

Con esto, desplegamos y ya podemos usarla en nuestros themes.

Enviar email con Telnet

Posted on septiembre 6th, 2011 in Linux | 218 Comments »

Cuando quiero probar un servidor de correo, me gusta hacerlo via Telnet, pero no suelo recordar los pasos y siempre busco en Google, así que aquí los dejo para futuras referencias (las líneas que comienzan con un número son la respuesta del servidor):

$ telnet [nombre servidor] [puerto]
220 [mensaje de bienvenida]
HELO [nombre de dominio]
250 [nombre de servidor]
MAIL FROM:[direcci&oacute;n del remitente]
250 [confirmaci&oacute;n de que la direcci&oacute;n es correcta]
RCPT TO:[direcci&oacute;n del destinatario]
250 [confirmaci&oacute;n de que la direcci&oacute;n es correcta]
DATA
354 End data with <CR><LF>.<CR><LF>
[Cuerpo del mensaje]<CR><LF>.<CR><LF>
250 [confirmaci&oacute;n de recepci&oacute;n correcta]
QUIT
221 [Mensaje de despedida]

Liferay pop up con Alloy

Posted on septiembre 1st, 2011 in Liferay | No Comments »

En versiones anteriores de Liferay se usaba Liferay.Popup para hacer popups usando jQuery. Ahora, con la nueva versión, deberemos usar Alloy UI.

Mostrar contenido html en un popup:

<aui:script>
function callPopup(){
    AUI().ready('aui-dialog', 'aui-overlay-manager', 'dd-constrain', function(A) {
        var dialog = new A.Dialog({
            title: 'DISPLAY CONTENT',
            centered: true,
            modal: true,
            width: 500,
            height: 400,
            bodyContent: "This is testing content inside the popup"
        }).render();
    });
}
</aui:script>

Pasar una URL al Popup:

<aui:script>
Liferay.provide(window,'<portlet:namespace />callPopup',
    function(url1) {
        var A = AUI();
        var data = {};
        var dialog = new A.Dialog(
            {
                centered: true,
                destroyOnClose: true,
                modal: true,
                title: Liferay.Language.get('Display-Content'),
                width: 600
            }
        ).render();
        dialog.plug(
            A.Plugin.IO,
            {
                data: data,
                uri: url1
            }
        );
    },
    ['aui-dialog', 'aui-io']
);
</aui:script>