diff --git a/api/current.txt b/api/current.txt index 3ebd522cc0c98..9492d566a30eb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15764,6 +15764,7 @@ package android.nfc { method public static android.nfc.NdefRecord createApplicationRecord(java.lang.String); method public static android.nfc.NdefRecord createExternal(java.lang.String, java.lang.String, byte[]); method public static android.nfc.NdefRecord createMime(java.lang.String, byte[]); + method public static android.nfc.NdefRecord createTextRecord(java.lang.String, java.lang.String); method public static android.nfc.NdefRecord createUri(android.net.Uri); method public static android.nfc.NdefRecord createUri(java.lang.String); method public int describeContents(); @@ -15800,6 +15801,7 @@ package android.nfc { method public deprecated void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage); method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle); method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context); + method public boolean invokeBeam(android.app.Activity); method public boolean isEnabled(); method public boolean isNdefPushEnabled(); method public void setBeamPushUris(android.net.Uri[], android.app.Activity); diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index 10988c6bb8c36..635a50fd31540 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 de481cf5642cc..654871252240c 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; @@ -473,6 +474,45 @@ public final class NdefRecord implements Parcelable { return new NdefRecord(TNF_EXTERNAL_TYPE, b, null, data); } + /** + * 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 diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java index 77c0234991388..8643f2ef0802e 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 e8b74377910d2..6751a523312dd 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -1248,6 +1248,45 @@ public final class NfcAdapter { mNfcActivityManager.disableReaderMode(activity); } + /** + * 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. *