// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2012-2014 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
//package com.google.appinventor.components.runtime;
package mifare.uid;
import com.google.appinventor.components.runtime.*;
import android.app.Activity;
import android.content.Intent;
//import android.app.PendingIntent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.util.Log;
import android.nfc.tech.MifareClassic;
//added these to have a log file to undestand what is executed and what no
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
//
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.DesignerProperty;
import com.google.appinventor.components.annotations.PropertyCategory;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.annotations.UsesActivities;
import com.google.appinventor.components.annotations.androidmanifest.*;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.PropertyTypeConstants;
import com.google.appinventor.components.common.YaVersion;
import com.google.appinventor.components.runtime.util.GingerbreadUtil;
import com.google.appinventor.components.runtime.util.SdkLevel;
/**
* Non-visible component to provide NFC capabilities. For now this component supports the reading
* and writing of text tags only (if supported by the device).
*
* In order to read and write text tags, the component must have its {@link #ReadMode(boolean)}
* property set to `true`{:.logic.block} or `false`{:.logic.block} respectively.
*
* **Note:** This component will only work on Screen1 of any App Inventor app.
*/
@DesignerComponent(version = YaVersion.NEARFIELD_COMPONENT_VERSION,
description = "
Non-visible component to provide NFC capabilities. " +
"For now this component supports the reading and writing of text tags only " +
"(if supported by the device)
" +
"In order to read and write text tags, the component must have its " +
"ReadMode
property set to True or False respectively.
" +
"Note: This component will only work on Screen1 of" +
" any App Inventor app.
",
category = ComponentCategory.EXTENSION,
nonVisible = true,
iconName = "images/nearfield.png")
@SimpleObject(external=true)
@UsesPermissions(permissionNames = "android.permission.NFC,android.permission.WRITE_EXTERNAL_STORAGE")
/*
@UsesActivities(
activities = {
@ActivityElement(
intentFilters = {
@IntentFilterElement(
actionElements = {
@ActionElement(
name = "android.nfc.action.TAG_DISCOVERED"
)
},
categoryElements = {
@CategoryElement(
name = "android.intent.category.DEFAULT"
)
}
)
}
,name=".Screen1"
)
}
)
*/
//after create the apk i edit the manifest adding manually this lines because i didn't find the right way to add this directly from the extension,
//this enable the app to receive the tag discovered
//
//
//
//
//this should resolve the problem of the onNewIntent but....
//android:launchMode="singleTop"
public class MifareUid extends AndroidNonvisibleComponent
implements OnStopListener, OnResumeListener, OnPauseListener, OnNewIntentListener, Deleteable {
private static final String TAG = "nearfield";
private Activity activity;
private NfcAdapter nfcAdapter;
//private PendingIntent pendingIntent;
private boolean readMode = true;
private int writeType;
private String tagContent = "";
private String textToWrite = "";
/* Used to identify the call to startActivityForResult. Will be passed back into the
resultReturned() callback method. */
protected int requestCode;
/**
* Creates a new NearField component
* @param container ignored (because this is a non-visible component)
*/
public MifareUid(ComponentContainer container) {
super(container.$form());
activity = container.$context();
writeType = 1;
nfcAdapter = (SdkLevel.getLevel() >= SdkLevel.LEVEL_GINGERBREAD)
? GingerbreadUtil.newNfcAdapter(activity)
: null;
// register with the forms to that OnResume and OnNewIntent
// messages get sent to this component
form.registerForOnResume(this);
form.registerForOnNewIntent(this);
form.registerForOnPause(this);
Log.d(TAG, "Nearfield component created");
appendLog("Nearfield component created");
//Add PendingIntent as advised on but they add the pendingintent in the onStart, that I don't have, so how to use it?
//pendingIntent = PendingIntent.getActivity(this,0,new Intent(this,this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),0);
}
// Events
/**
* Indicates that a new tag has been detected.
* Currently this is only a plain text tag, as specified in the
* manifest.
* @internaldoc
* See Compiler.java.
*/
@SimpleEvent
public void TagRead(String message) {
Log.d(TAG, "Tag read: got message " + message);
appendLog("Tag read: got message " + message);
tagContent = message;
EventDispatcher.dispatchEvent(this, "TagRead", message);
}
// Properties
/**
* Returns the content of the most recently received tag.
*/
@SimpleProperty(category = PropertyCategory.BEHAVIOR)//what does this mean?
public String LastMessage() {
Log.d(TAG, "String message method stared");
appendLog("String message method stared");
return tagContent;
}
/**
* Returns true if in read mode, false if in write mode.
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN,
defaultValue = "True")
@SimpleProperty(category = PropertyCategory.BEHAVIOR)
public boolean ReadMode() {
Log.d(TAG, "boolean method stared");
appendLog("boolean method stared");
return readMode;
}
/**
* Specifies whether the NFC hardware should operate in read mode (`true`{:.logic.block}) or
* write mode (`false`{:.logic.block}).
*/
@SimpleProperty(category = PropertyCategory.BEHAVIOR,
description = "Specifies whether the NFC hardware should operate in read or write mode.")
public void ReadMode(boolean newMode) {
Log.d(TAG, "Read mode set to" + newMode);
readMode = newMode;
if(!readMode && SdkLevel.getLevel() >= SdkLevel.LEVEL_GINGERBREAD){
GingerbreadUtil.enableNFCWriteMode(activity, nfcAdapter, textToWrite);
}
}
@Override
public void onNewIntent(Intent intent) {
Log.d(TAG, "Nearfield on onNewIntent. Intent is: " + intent);
appendLog("Nearfield on onNewIntent. Intent is: " + intent);
//added as advised but commented because I have cannot find symbol when compile wit ant
//super.onNewIntent(intent);
//setIntent(intent);
resolveIntent(intent);
}
// TODO: Re-enable NFC communication if it had been disabled
@Override
public void onResume() {
//added as advised but commented because I have cannot find symbol when compile wit ant
//super.onResume();
//assert nfcAdapter != null;
//nfcAdapter.enableForegroundDispatch(this,pendingIntent,null,null);
Intent intent = activity.getIntent();
Log.d(TAG, "Nearfield on onResume. Intent is: " + intent);
appendLog("Nearfield on on onResume. Intent is: " + intent);
}
void resolveIntent(Intent intent) {
Log.d(TAG, "resolve intent. Intent is: " + intent);
appendLog("resolve intent. Intent is: " + intent);
// Parse the intent
// if (intent.getAction().equals(NfcAdapter.ACTION_TAG_DISCOVERED)) {
// tagContent = ByteArrayToHexString(intent.getByteArrayExtra(NfcAdapter.EXTRA_ID));
// }
String action = intent.getAction();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
Tag tag = (Tag) intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
appendLog("TAG: " + tag.getId());
appendLog("TAG: " + ByteArrayToHexString(tag.getId()));
assert tag != null;
byte[] payload = detectTagData(tag).getBytes();
}
else {
appendLog("NO!!!!!!! ACTION_NDEF_DISCOVERED_DISCOVERED");
}
}
private String detectTagData(Tag tag) {
String sb = "";
byte[] id = tag.getId();
sb = ByteArrayToHexString(id);
String prefix = "android.nfc.tech.";
for (String tech : tag.getTechList()) {
sb = sb +" "+ (tech.substring(prefix.length()));
sb = sb +", ";
}
for (String tech : tag.getTechList()) {
if (tech.equals(MifareClassic.class.getName())) {
String type = "Unknown";
try {
MifareClassic mifareTag = MifareClassic.get(tag);
switch (mifareTag.getType()) {
case MifareClassic.TYPE_CLASSIC:
type = "Classic";
break;
case MifareClassic.TYPE_PLUS:
type = "Plus";
break;
case MifareClassic.TYPE_PRO:
type = "Pro";
break;
}
sb = sb + "Mifare Classic type: ";
sb = sb + type;
} catch (Exception e) {
sb = "Mifare classic error: " + e.getMessage();
}
}
}
TagRead(sb);
return sb;
}
public void onPause() {
Log.d(TAG, "OnPause method started.");
/*
if (nfcAdapter != null) {
GingerbreadUtil.disableNFCAdapter(activity, nfcAdapter);
}
*/
//super.onPause();
//Onpause stop listening
if (nfcAdapter != null) {
nfcAdapter.disableForegroundDispatch(activity);
}
//nfcAdapter.disableForegroundDispatch(activity);
}
@Override
public void onDelete() {
// TODO Auto-generated method stub
// need to delete the nearfieldActivity
}
@Override
public void onStop() {
// TODO Auto-generated method stub
}
private String ByteArrayToHexString(byte [] inarray) {
int i, j, in;
String [] hex = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
String out= "";
for(j = 0 ; j < inarray.length ; ++j)
{
in = (int) inarray[j] & 0xff;
i = (in >> 4) & 0x0f;
out += hex[i];
i = in & 0x0f;
out += hex[i];
}
return out;
}
public void appendLog(String text)
{
File logFile = new File("sdcard/MyNFCLog.txt");
if (!logFile.exists())
{
try
{
logFile.createNewFile();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try
{
//BufferedWriter for performance, true to set append to file flag
BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true));
buf.append(text);
buf.newLine();
buf.flush();
buf.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}