Como vimos en la tercera entrada, desde una pantalla formada por varias questions podíamos realizar una comunicación entre ellas mediante eventos externos, lo cual es bastante útil, sin embargo aun es mas útil capturar eventos externos que realmente son externos al cliente de Movilizer y que son lanzados desde otras aplicaciones.
Esta funcionalidad es muy útil cuando el cliente de Movilizer no permite realizar cierta tarea por sí mismo. Por ejemplo es capaz de manejar la cámara de fotos y conexiones bluetooth, pero no puede comunicarse con un escáner láser o con un lector NFC ya sea por USB conectado al ordenador o internos en una PDA. (Me refiero a una comunicación por comunicación Serie, Socket, etc, por comunicación HID sí pero ahí no tenemos control alguno sobre el hardware).
Pues bien, esa limitación de Movilizer se puede solventar mediante aplicaciones programadas para tal fin, la idea es que Movilizer se conecte a esa aplicación y esta a su vez al dispositivo de hardware haciendo de este modo de intermediaria. Generalmente estas aplicaciones intermediarias tienen como uso mas común el manejo de hardware, eso no quita que se le pueda dar otro uso para realizar cualquier otra funcionalidad que el lenguaje de programación de Movilizer no pudiera realizar de serie, me viene a la memoria el uso que le dimos en el hackathon en el que participe en Alemania:
en el cual necesitábamos una aplicación que funcionase de timer y que cada 1 segundo lanzase un evento externo hacia Movilizer para que este a su vez pintase unas gráficas que se iban actualizando de este modo… Movilizer solo cuenta con pausas/delays pero aun no con timers, otro uso mas interesante y complejo podría ser el tratamiento de imágenes o tal vez la creación de PDFs con firma digital con el lector de huellas.
Centrándonos en el asunto, la idea de esta entrada es la de ver como hacer una aplicación en Android que reciba parámetros desde Movilizer, la aplicación lea un tag NFC mediante el lector del móvil, y que después devuelva el ID a Movilizer mediante un evento externo. ¿Y por qué este ejemplo concreto? pues porque ya lo hice en mi tiempo libre porque tenia mono de programar en Android, por eso quiero repetir la misma app pero esta vez haciéndola extremadamente simplificada y sin adornos de ningún tipo, sé de alguno del trabajo que me lo agradecera 😉
La explicación se va a dividir en dos vídeos, el primero muestro como funciona todo el tinglado en la app que tengo actualmente programada, y en el segundo vídeo muestro como hacer la app simplificada, la cual esta adjunta mas abajo como enlace a GitHub.
Código del proyecto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
package com.giltesa.nfcsimple.activity; import android.annotation.SuppressLint; import android.app.PendingIntent; import android.content.Intent; import android.content.IntentFilter; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Handler; import android.provider.Settings; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.Toast; import com.giltesa.nfcsimple.R; import com.giltesa.nfcsimple.util.Movilizer; import com.giltesa.nfcsimple.util.Preferences; import com.giltesa.nfcsimple.util.TryParse; import org.json.JSONObject; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; public class MainActivity extends AppCompatActivity { private Movilizer movilizer; private NfcAdapter nfcAdapter; private PendingIntent pendingIntent; private IntentFilter writeTagFilters[]; /** * @param savedInstanceState */ @Override protected void onCreate( Bundle savedInstanceState ) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent moviIntent = getIntent(); if( moviIntent != null && Intent.ACTION_VIEW.equals(moviIntent.getAction()) ) { Preferences pref = new Preferences(this); // Get input parameters from Movilizer for configure the handler: String eventID = null; String eventType = null; boolean runHandler = true; try { JSONObject json = new JSONObject(moviIntent.getDataString()); eventID = TryParse.tryParseString(json, "EVENT_ID", ""); eventType = TryParse.tryParseString(json, "EVENT_TYPE", "0"); } catch( Exception e ) { runHandler = false; Log.e(Preferences.LOG_TAG, getString(R.string.toast_fatal_error), e); Toast.makeText(this, getString(R.string.toast_fatal_error), Toast.LENGTH_SHORT).show(); } if( runHandler ) { //Check MEL mandatory parameters: if( eventID == null || eventID.isEmpty() ) { Log.e(Preferences.LOG_TAG, getString(R.string.toast_event_id_field_missing)); Toast.makeText(this, getString(R.string.toast_event_id_field_missing), Toast.LENGTH_SHORT).show(); finish(); } //OK: else { pref.setEventID(eventID); pref.setEventType(eventType); pref.setMovilizerClient(getReferrer().getHost()); movilizer = new Movilizer(this, new Handler()); nfcAdapter = NfcAdapter.getDefaultAdapter(this); if( nfcAdapter == null ) { Toast.makeText(this, getString(R.string.toast_nfc_not_available), Toast.LENGTH_SHORT).show(); finish(); return; } pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED); tagDetected.addCategory(Intent.CATEGORY_DEFAULT); writeTagFilters = new IntentFilter[]{tagDetected}; } } } } /** * @param intent */ @SuppressLint("NewApi") protected void onNewIntent( Intent intent ) { if( NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction()) ) { Tag myTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); String id = new BigInteger(1, myTag.getId()).toString(); Map<String, Object> values = new HashMap<>(); values.put("ID", id); movilizer.doSendMessage(values); finish(); } } /** * */ public void onPause() { super.onPause(); if( nfcAdapter != null ) { nfcAdapter.disableForegroundDispatch(this); } } /** * */ public void onResume() { super.onResume(); if( nfcAdapter != null ) { if( !nfcAdapter.isEnabled() ) { Toast.makeText(this, getString(R.string.toast_nfc_disabled), Toast.LENGTH_SHORT).show(); startActivity(new Intent(Settings.ACTION_NFC_SETTINGS)); finish(); return; } nfcAdapter.enableForegroundDispatch(this, pendingIntent, writeTagFilters, null); } } } |
Código de la Movelet:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
<MovilizerRequest systemId="${#Project#SystemID}" systemPassword="${#Project#Password}" xmlns="http://movilitas.com/movilizer/v16"> <moveletDelete moveletKey="SCN_NFC_SIMPLE" /> <moveletSet> <movelet moveletKey="SCN_NFC_SIMPLE" moveletType="MULTI" initialQuestionKey="SCR_FORM" transactional="true" priority="-2"> <question key="SCR_FORM" type="5" title="Movilizer NFC Handler" labelFontStyle="BOLD"> <answer key="ANS_FORM_NFC_ID" nextQuestionKey="CANCEL" attributeType="-128" position="1" columnSizeType="ROWS"> <text>NFC ID:</text> </answer> <answer key="ANS_FORM_BTN_READ_TAG" nextQuestionKey="CANCEL" attributeType="14" position="2" columnSizeType="ROWS" labelAlignment="CENTER" onScreenValueChangeEventTrigger="SYNCHRONOUS"> <text>Press to read the Tag</text> </answer> <validation type="ERROR"> <condition>isBlank(nfcID)</condition> <text>Read the Tag to continue!</text> </validation> <onEnterAssignment> nfcID = null; </onEnterAssignment> <onLeaveOkPrepareAssignment> call($local:disconnectNFC)(); nfcID = getAnswerValueByClientKey($answer:"ANS_FORM_NFC_ID", null); </onLeaveOkPrepareAssignment> <onLeaveBackAssignment> call($local:disconnectNFC)(); </onLeaveBackAssignment> <onScreenValueChangeEvent> function( $ref:answerKey, $ref:clientKey, $ref:value, $ref:data ) { if( answerKey == $answer:"ANS_FORM_BTN_READ_TAG" ) { call($local:connectNFC)(); } } </onScreenValueChangeEvent> <onExternalEvent> function( $ref:evtSrc, $ref:data ) { if( evtSrc == $local:eventID ) { value = data["ID"]; <!-- Decimal value --> if( isBlank(value) ) { text = "The read value is not valid"; } else { setAnswerValueByClientKey($answer:"ANS_FORM_NFC_ID", null, value); } call($local:disconnectNFC)(); } } </onExternalEvent> </question> <syncDownloadAssignment> $local:connectNFC = function() { $local:eventID = 1234; package = "com.giltesa.nfcsimple"; parameters = { "EVENT_ID" : $local:eventID ; <!-- Number: (mandatory) --> "EVENT_TYPE" : 0 <!-- Number: 0=Synchronous(default) | 1=Asynchronous Guaranteed | 2=Asynchronous --> }; intentURL = concat("%", package, "%", objectToJson(parameters)); $local:conID = connect(concat("exec:", intentURL), "name"); if( !isConnectionOpen($local:conID) ) { showProgress("Connection error!"); sleep(3000); } else { subscribeExternalEventId($local:eventID); } }; $local:disconnectNFC = function() { if( isConnectionOpen($local:conID) ) { unsubscribeExternalEventId($local:eventID); close($local:conID); } }; </syncDownloadAssignment> <name>NFC Simple</name> </movelet> <participant participantKey="Test1" name="Test1" deviceAddress="${#Project#Participant}" /> </moveletSet> </MovilizerRequest> |