From 7fe9fa163ccd2ec34cbf533be4422aa66356431f Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Wed, 29 Jan 2014 17:28:04 -0800 Subject: Add an API to manually invoke Android Beam. Activities can call NfcAdapter.invokeBeam() to manually invoke the Beam animation. Any NFC tap is then enough to complete the transaction. Also, added NdefRecord convenience method for creating Text records. Bug: 5134061 Change-Id: Ia9df360d1d7e8451157c85a6d12f2a4eec924960 --- core/java/android/nfc/INfcAdapter.aidl | 1 + core/java/android/nfc/NdefRecord.java | 40 +++++++++++++++++++++++++++ core/java/android/nfc/NfcActivityManager.java | 11 +++++++- core/java/android/nfc/NfcAdapter.java | 39 ++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) (limited to 'core/java') diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index 10988c6..635a50f 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -48,6 +48,7 @@ interface INfcAdapter void setForegroundDispatch(in PendingIntent intent, in IntentFilter[] filters, in TechListParcel techLists); void setAppCallback(in IAppCallback callback); + void invokeBeam(); void dispatch(in Tag tag); diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java index de481cf..6548712 100644 --- a/core/java/android/nfc/NdefRecord.java +++ b/core/java/android/nfc/NdefRecord.java @@ -20,6 +20,7 @@ import android.content.Intent; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; + import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -474,6 +475,45 @@ public final class NdefRecord implements Parcelable { } /** + * Create a new NDEF record containing UTF-8 text data.

+ * + * The caller can either specify the language code for the provided text, + * or otherwise the language code corresponding to the current default + * locale will be used. + * + * Reference specification: NFCForum-TS-RTD_Text_1.0 + * @param languageCode The languageCode for the record. If locale is empty or null, + * the language code of the current default locale will be used. + * @param text The text to be encoded in the record. Will be represented in UTF-8 format. + * @throws IllegalArgumentException if text is null + */ + public static NdefRecord createTextRecord(String languageCode, String text) { + if (text == null) throw new NullPointerException("text is null"); + + byte[] textBytes = text.getBytes(StandardCharsets.UTF_8); + + byte[] languageCodeBytes = null; + if (languageCode != null && !languageCode.isEmpty()) { + languageCodeBytes = languageCode.getBytes(StandardCharsets.US_ASCII); + } else { + languageCodeBytes = Locale.getDefault().getLanguage(). + getBytes(StandardCharsets.US_ASCII); + } + // We only have 6 bits to indicate ISO/IANA language code. + if (languageCodeBytes.length >= 64) { + throw new IllegalArgumentException("language code is too long, must be <64 bytes."); + } + ByteBuffer buffer = ByteBuffer.allocate(1 + languageCodeBytes.length + textBytes.length); + + byte status = (byte) (languageCodeBytes.length & 0xFF); + buffer.put(status); + buffer.put(languageCodeBytes); + buffer.put(textBytes); + + return new NdefRecord(TNF_WELL_KNOWN, RTD_TEXT, null, buffer.array()); + } + + /** * Construct an NDEF Record from its component fields.

* Recommend to use helpers such as {#createUri} or * {{@link #createExternal} where possible, since they perform diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java index 77c0234..8643f2e 100644 --- a/core/java/android/nfc/NfcActivityManager.java +++ b/core/java/android/nfc/NfcActivityManager.java @@ -18,6 +18,7 @@ package android.nfc; import android.app.Activity; import android.app.Application; +import android.content.Intent; import android.net.Uri; import android.nfc.NfcAdapter.ReaderCallback; import android.os.Binder; @@ -327,6 +328,7 @@ public final class NfcActivityManager extends IAppCallback.Stub NfcAdapter.CreateNdefMessageCallback ndefCallback; NfcAdapter.CreateBeamUrisCallback urisCallback; NdefMessage message; + Activity activity; Uri[] uris; int flags; synchronized (NfcActivityManager.this) { @@ -338,6 +340,7 @@ public final class NfcActivityManager extends IAppCallback.Stub message = state.ndefMessage; uris = state.uris; flags = state.flags; + activity = state.activity; } // Make callbacks without lock @@ -362,7 +365,13 @@ public final class NfcActivityManager extends IAppCallback.Stub } } } - + if (uris != null && uris.length > 0) { + for (Uri uri : uris) { + // Grant the NFC process permission to read these URIs + activity.grantUriPermission("com.android.nfc", uri, + Intent.FLAG_GRANT_READ_URI_PERMISSION); + } + } return new BeamShareData(message, uris, flags); } diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index e8b7437..6751a52 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -1249,6 +1249,45 @@ public final class NfcAdapter { } /** + * Manually invoke Android Beam to share data. + * + *

The Android Beam animation is normally only shown when two NFC-capable + * devices come into range. + * By calling this method, an Activity can invoke the Beam animation directly + * even if no other NFC device is in range yet. The Beam animation will then + * prompt the user to tap another NFC-capable device to complete the data + * transfer. + * + *

The main advantage of using this method is that it avoids the need for the + * user to tap the screen to complete the transfer, as this method already + * establishes the direction of the transfer and the consent of the user to + * share data. Callers are responsible for making sure that the user has + * consented to sharing data on NFC tap. + * + *

Note that to use this method, the passed in Activity must have already + * set data to share over Beam by using method calls such as + * {@link #setNdefPushMessageCallback(CreateNdefMessageCallback, Activity, Activity...)} or + * {@link #setBeamPushUrisCallback(CreateBeamUrisCallback, Activity)}. + * + * @param activity the current foreground Activity that has registered data to share + * @return whether the Beam animation was successfully invoked + */ + public boolean invokeBeam(Activity activity) { + if (activity == null) { + throw new NullPointerException("activity may not be null."); + } + enforceResumed(activity); + try { + sService.invokeBeam(); + return true; + } catch (RemoteException e) { + Log.e(TAG, "invokeBeam: NFC process has died."); + attemptDeadServiceRecovery(e); + return false; + } + } + + /** * Enable NDEF message push over NFC while this Activity is in the foreground. * *

You must explicitly call this method every time the activity is -- cgit v1.1