Mi App Android en el Play Store: Alolan Pokédex

Recientemente he desarrollado y publicado una app muy sencilla, pero práctica para los jugadores de la más reciente entrega de Pokémon:  Sun y Moon. Se trata de una aplicación de referencia, que lista los diferentes Pokemon capturables en ambos juegos, junto con su respectivo Código QR.

Funcionalidad QR en Sun/Moon

El escanear un Código QR con la Cámara del Nintendo 3DS, permite obtener el registro del Pokemon respectivo, sin necesidad de capturarlo. Esto, entre otras cosas permite que se vea la ubicación exacta donde podemos encontrar a esa determinada especie en su forma salvaje, para poder capturar uno propio.

La idea de la aplicación surgió mientras hacia uso de esta misma característica, pero consultando desde el álbum del sitio web ImGur, donde se publicaron originalmente estos códigos. El proceso de buscar el QR deseado usando este sitio, se me apetecía tediosa y complicada: No existe manera de cargar todas las imágenes a la vez en la ventana del navegador, por lo que una búsqueda con CTRL+F no siempre arrojaba el resultado esperado.

El álbum en Imgur

Fué entonces cuando se me ocurrió descargarlas todas. Pero había un problema: Son mas de 400 imágenes y el nombre del archivo es un texto aleatorio que no sirve para identificar al Pokemon. Y el proceso de guardar manualmente 400 imágenes de internet una a una simplemente no era una opción.

Guardar uno por uno? No gracias!

Como todo buen programador, sabía que debía haber una mejor manera de hacer esta tarea, sólo hacía falta encontrar la herramienta. Fue entonces cuando pude darme cuenta de algo muy importante: El atributo ALT (descripción alternativa) de cada una de las imágenes contiene el número, seguido del nombre de cada Pokemon.  Fue entonces que lo tuve claro. Solo necesitaba una herramienta que permitiese descargar todas las imágenes de un sitio web, usando su atributo ALT como nombre de imagen.

Como se puede ver, el SRC de la imagen es un texto al azar. Pero el ALT es justo lo que necesito!

Luego de buscar en la web, me di cuenta que muchas otras personas han tenido esta necesidad en el pasado, pero sin embargo no existe en el mercado una herramienta que lo haga. Debía hacer mi propia herramienta…

Fué en este momento que encontré una extensión del navegador Google Chrome que permite descargar todas las imágenes de una página web. Y lo mejor: Es de código abierto!. Mi misión era sencilla: Modificar la extensión para que al descargar, en vez del nombre original del archivo, use el nombre del atributo ALT de la imagen en cuestión.

«Image Downloader» en la Chrome Web Store

Asi que aquí estaba, con el código fuente del proyecto original , ahora bifurcado a mi repositorio local. Tuve que aprender cómo se desarrolla una extensión para navegador: El acceso al DOM, el uso de la configuración, el manejo de las acciones, la interacción entre módulos y la instalación local en modo desarrollador, entre otros aspectos muy técnicos.

Al final, solo necesité crear un arreglo con el texto ALT de cada imagen al cual el API tiene acceso, para luego pasarlo a la función que genera las descargas automáticas, como parámetro ‘filename'.

La función que captura el atributo ALT en todas las imágenes.

Para quienes están interesados en ver todos los cambios requeridos, o descargar mi versión de esta extensión. (Que he llamado ALT Image Downloader), el código esta en mi GitHub, desde donde también se pueden ver las diferencias con la rama original.

Una vez que tuve las imágenes descargadas justo como las necesitaba, solo bastó editarlas, generar un listado, crear una base de datos, construir un API, subir todos los recursos al hosting y por supuesto: Desarrollar y publicar el App Android!. Sin embargo, esos detalles los comentaré en la entrega número dos de ésta entrada.

Captura del App: Alolan Pokedex

«Gotta catchem alljust not by hand».

Android: Aplicación sencilla con back-end en Firebase.

Firebase es una plataforma para el desarrollo de aplicaciones web y aplicaciones móviles adquirida por Google en 2014. En este Post realizaremos algunas un tutorial para hacer uso de los beneficios de FireBase.

Aplicación de registro y lectura de mensajes desde el servidor.

Una aplicación Android puede operar perfectamente sin acceso a información a través de internet. Éste tipo de aplicaciones se conocen como aplicaciones “Autónomas” o “Standalone”. Ejemplos de éste tipo de aplicaciones hay muchos: Calculadoras, Alarmas, Editores, Juegos. Sin embargo, muchas de las aplicaciones requieren de algún tipo de acceso a internet (bien sea para realizar alguna conexión a bases de datos, o consumir un servicio web). En esta guía veremos cómo realizar (y hospedar en la nube) nuestro desarrollo a nivel de servidor (backend) en Firebase.
Firebase Logo

El tutorial completo se encuentra en el documento drive adjunto!.

Actualizar Android Studio a V2.0

Android Studio 2.0 ya está disponible, y ahora que es estable, se puede actualizar de manera segura a la nueva versión. Tiene algunas características interesantes, pero la más importante es que ahora los emuladores son mucho más rápidos de usar cuando corremos nuestras aplicaciones.

Aquí está el proceso para actualizar:

1. Inicia Android Studio. Debes ver una ventana emergente explicando que existe una versión nueva. Haz click y deberías ver esta pantalla:

1
Selecciona «Actualizar y reiniciar» para comenzar el proceso.

2. Durante la actualización, es posible que veas la siguiente ventana:

2
Si aparece, solo selecciona «Siguiente».

3. Android studio comenzará a Descargarse.

3
Deja que finalice de actualizar.

4. Deberías ver esta ventana emergente la primera vez que abras Android Studio después de actualizar:

4
Elige la primera opción para que tus configuraciones se mantengan.

5. Puede que veas esta pantalla.

5
Te está recomendando que deberías usar la ultima versión del JDK. Sugiero que actualices (es gratis).

Ésta es la última versión del JDK, directo desde Oracle: http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

6
Último JDK.

6. También puede que veas esta pantalla:

7
Selecciona «Actualizar».

7. Verás la siguiente ventana:

8
Selecciona el android-sdk-license y deja que la instalación finalice.

8. Por cada proyecto existente que abras luego de actualizar, verás esta ventana emergente aparecer:

9
Selecciona «Actualizar» y deja que haga su trabajo.

9. Eventualmente verás esto en pantalla:

10
Selecciona «Fix Gradle wrapper and re-import project». Una vez finalizado, estás listo para comenzar a desarrollar!.

10. Aunque no es absolutamente necesario, sugiero borrar y crear nuevamente cualquier emulador AVD (Android Virtual Devices) que tengas configurado. De ésta manera nos aseguramos de contar con los beneficios de emulación añadidos.

Listo! A programar…

Aplicacion para SMS premium

Los SMS premium están en franca decadencia. Sin embargo, dependiendo de la calidad del servicio provisto, todavía es posible obtener una ganancia decente.

Un cliente me planteó la idea de crear una aplicación Android que permita precisamente esto: Enviar una respuesta automáticamente a los SMS recibidos, de acuerdo al contenido del mismo. Por ejemplo, si se recibe el texto Triple, se envíe una respuesta apropiada.

Para ello, obviamente se requiere una Base de Datos que pueda ser modificada en tiempo real, para permitir cierto dinamismo en las respuestas. He decidido usar el excelente servicio prestado por Parse.com ya que facilita el trabajo. Para mayor detalle, siempre se puede consultar la documentación.

Parse.com class with object details
Parse.com class with object details

Por último, es de hacer notar que desde Android 4.4, ninguna aplicacion puede tener el control total de de los SMS a menos que el usuario seleccione nuestra aplicacion como la aplicacion de SMS por defecto. Pero esto se puede evitar usando simplemente SmsManager

El Gist en GiHub:


package com.leonelatencio.mysms;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.provider.Telephony;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Log;
import com.parse.FindCallback;
import com.parse.GetCallback;
import com.parse.ParseException;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import java.util.List;
/**
* Created by Leonel on 18-04-2015.
*/
public class SmsListener extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
for (SmsMessage smsMessage : Telephony.Sms.Intents.getMessagesFromIntent(intent)) {
final String messageBody = smsMessage.getMessageBody();
final String messageFrom = smsMessage.getOriginatingAddress();
//Log Received SMS
Log.d("SMS", "Message received: " + messageBody + " From: " + messageFrom);
//Save received SMS to Parse
ParseObject testObject = new ParseObject("SMSRec");
testObject.put("From", messageFrom);
testObject.put("Message", messageBody);
testObject.saveInBackground();
//Query Reply Message in Parse
ParseQuery<ParseObject> query = ParseQuery.getQuery("SMSContent");
query.whereEqualTo("keyWord", messageBody);
query.getFirstInBackground(new GetCallback<ParseObject>() {
public void done(ParseObject object, ParseException e) {
if (object == null) {
Log.d("SMS", "Cant find a response.");
} else {
String responseBody = object.getString("responseText");
Log.d("SMS", "Found a response to" + messageBody + ":/n" + responseBody);
//Finally, try to send SMS.
sendSMS(messageFrom, responseBody);
}
}
});
}
}
}
public void sendSMS(String replyTo, String replyBody){
try {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(replyTo, null, replyBody, null, null);
//Log sent message.
Log.d("SMS", "Sending message: " + replyBody + " To: " + replyTo);
} catch (Exception ex) {
//Log failed SMS.
Log.d("SMS", "Sending failed: " + replyBody + " To: " + replyTo);
}
}
}

Main Activity
Main Activity

Android Custom Adapter con Fragments

ListView view of app
ListView view of app

He estado tomando un par de cursos online sobre Desarrollo de Aplicaciones Android, para ayudarme en un proyecto que tengo en mente, usando un Custom Drawer para navegar entre secciones (Fragments) y usando Parse.com como backend para la obtencion de la data. Les dejo un mini-tutorial.

main_fragment.xml
Este es el fragmento que se necesita mostrar. Tiene un TextView en la parte superior para usar como titulo
This is the fragment you need to show on the app. Notice the ListView has an ID. It also has a TextView to show headlines



    


    

    


adapter_layout.xml
Este es el diseno que nuestro ‘Custom Adapter’ usara para insertar la data a cada Fila de nuestro ‘ListView’
This is the layout our custom adapter is going to use to fetch the data into each Row in our ListView. The style here is going to show as each item for the main List

 

    

    

 

CustomAdapter.java
Este es nuestro adaptor. Usa el objeto placesObject para colocar el texto en los campos de adapter_layout.xml
This is our custom adapter that uses a ParseObject placesObject to set the text of our adapter_layout.xml fields

/**
 * Custom Adapter for Places List
 * Created by Leonel on 01-01-2015.
 */

public class PlacesAdapter extends ArrayAdapter {
    protected Context mContext;
    protected List mPlaces;

    public PlacesAdapter(Context context, List place) {
        super(context, R.layout.places_custom_fragment, place);
        mContext = context;
        mPlaces = place;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(
                    R.layout.places_custom_fragment, null);
            holder = new ViewHolder();
            holder.placeName = (TextView) convertView
                    .findViewById(R.id.txtPlace);
            holder.placeDescription = (TextView) convertView
                    .findViewById(R.id.txtPlaceDescription);

            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        ParseObject placesObject = mPlaces.get(position);

        // title
        String place = placesObject.getString("placeName");
        holder.placeName.setText(place);

        // content
        String description = placesObject.getString("placeDescription");
        holder.placeDescription.setText(description);

        return convertView;
    }

    public static class ViewHolder {
        TextView placeName;
        TextView placeDescription;
    }
}

MainFragment.java
Creamos la consulta a Parse.com y lo enviamos a nuestro adaptor para ser expandido
In our onActivityCreated, we make our Parse.com query and send it to our adapter to be expanded.


public class PlacesFragment extends Fragment {

	private boolean mSearchCheck;
        protected List mPlaces;

        @Override
	public void onActivityCreated(Bundle savedInstanceState) {
        // TODO Populate ListView with Data from Parse

        //Query All Places

        final ListView mlist = (ListView)getView().findViewById(R.id.listMain);

        ParseQuery query = ParseQuery.getQuery("Places");
        query.findInBackground(new FindCallback() {
            @Override
            public void done(List place, ParseException e) {
                if (e == null) {
                    mPlaces = place;

                    PlacesAdapter adapter = new PlacesAdapter(getActivity(), mPlaces);
                    mlist.setAdapter(adapter);
                } else {
                }
            }
        });
}

Vínculo al proyecto en GitHub
Vinculo al curso en Udemy