Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.device.nfc; | |
| 6 | |
| 7 import android.Manifest; | |
| 8 import android.annotation.TargetApi; | |
| 9 import android.app.Activity; | |
| 10 import android.content.Context; | |
| 11 import android.content.pm.PackageManager; | |
| 12 import android.nfc.FormatException; | |
| 13 import android.nfc.NdefMessage; | |
| 14 import android.nfc.NdefRecord; | |
| 15 import android.nfc.NfcAdapter; | |
| 16 import android.nfc.NfcAdapter.ReaderCallback; | |
| 17 import android.nfc.NfcManager; | |
| 18 import android.nfc.Tag; | |
| 19 import android.nfc.TagLostException; | |
| 20 import android.nfc.tech.Ndef; | |
| 21 import android.nfc.tech.NdefFormatable; | |
| 22 import android.nfc.tech.TagTechnology; | |
| 23 import android.os.Build; | |
| 24 import android.os.Process; | |
| 25 import android.support.annotation.Nullable; | |
| 26 | |
| 27 import org.chromium.base.ApplicationStatus; | |
| 28 import org.chromium.base.Log; | |
| 29 import org.chromium.mojo.bindings.Callbacks; | |
| 30 import org.chromium.mojo.system.MojoException; | |
| 31 import org.chromium.mojom.device.Nfc; | |
| 32 import org.chromium.mojom.device.NfcClient; | |
| 33 import org.chromium.mojom.device.NfcError; | |
| 34 import org.chromium.mojom.device.NfcErrorType; | |
| 35 import org.chromium.mojom.device.NfcMessage; | |
| 36 import org.chromium.mojom.device.NfcPushOptions; | |
| 37 import org.chromium.mojom.device.NfcPushTarget; | |
| 38 import org.chromium.mojom.device.NfcRecord; | |
| 39 import org.chromium.mojom.device.NfcRecordType; | |
| 40 import org.chromium.mojom.device.NfcWatchOptions; | |
| 41 | |
| 42 import java.io.IOException; | |
| 43 import java.io.UnsupportedEncodingException; | |
| 44 import java.util.ArrayList; | |
| 45 import java.util.List; | |
| 46 | |
| 47 /** | |
| 48 * Android implementation of the NFC mojo service defined in | |
| 49 * device/nfc/nfc.mojom. | |
| 50 */ | |
| 51 public class NfcImpl implements Nfc { | |
| 52 private static final String TAG = "NfcImpl"; | |
| 53 private static final String DOMAIN = "w3.org"; | |
| 54 private static final String TYPE = "webnfc"; | |
| 55 private static final String TEXT_MIME = "text/plain"; | |
| 56 private static final String CHARSET_UTF8 = ";charset=UTF-8"; | |
| 57 private static final String CHARSET_UTF16 = ";charset=UTF-16"; | |
| 58 | |
| 59 /** | |
| 60 * Used to get instance of NFC adapter, @see android.nfc.NfcManager | |
| 61 */ | |
| 62 private final NfcManager mNfcManager; | |
| 63 | |
| 64 /** | |
| 65 * NFC adapter. @see android.nfc.NfcAdapter | |
| 66 */ | |
| 67 private final NfcAdapter mNfcAdapter; | |
| 68 | |
| 69 /** | |
| 70 * Context that is pasesed to NFC mojo service. Used to check permissions | |
|
Ted C
2016/04/28 17:27:22
line wrapping for comments should be 100 in java
shalamov
2016/05/11 14:09:57
Done.
| |
| 71 * and get Android NFC system service. | |
| 72 */ | |
| 73 private final Context mContext; | |
| 74 | |
| 75 /** | |
| 76 * Activity object that is requred to enable / disable NFC reader mode opera tions. | |
| 77 */ | |
| 78 private final Activity mActivity; | |
| 79 | |
| 80 /** | |
| 81 * Flag that indicates whether NFC permission is granted. | |
| 82 */ | |
| 83 private final boolean mHasPermission; | |
| 84 | |
| 85 /** | |
| 86 * Implementation of android.nfc.NfcAdapter.ReaderCallback. @see ReaderCallb ackHandler | |
| 87 */ | |
| 88 private ReaderCallbackHandler mReaderCallbackHandler; | |
| 89 | |
| 90 /** | |
| 91 * Object that contains data that was passed to method | |
| 92 * #push(NfcMessage message, NfcPushOptions options, PushResponse callback) | |
| 93 * @see PendingPushOperation | |
| 94 */ | |
| 95 private PendingPushOperation mPendingPushOperation; | |
| 96 | |
| 97 /** | |
| 98 * Utility that provides I/O operations for a Tag. Created on demand when | |
| 99 * Tag is found. @see NfcTagWriter | |
| 100 */ | |
| 101 private NfcTagWriter mTagWriter; | |
| 102 | |
| 103 public NfcImpl(Context context) { | |
| 104 mContext = context; | |
| 105 int permission = | |
| 106 context.checkPermission(Manifest.permission.NFC, Process.myPid() , Process.myUid()); | |
| 107 mHasPermission = permission == PackageManager.PERMISSION_GRANTED; | |
| 108 if (mHasPermission) { | |
| 109 mActivity = ApplicationStatus.getLastTrackedFocusedActivity(); | |
|
Ted C
2016/04/28 17:27:22
we should really avoid using this call if at all p
shalamov
2016/05/11 14:09:57
Done.
| |
| 110 mNfcManager = (NfcManager) mContext.getSystemService(Context.NFC_SER VICE); | |
| 111 if (mNfcManager != null) { | |
| 112 mNfcAdapter = mNfcManager.getDefaultAdapter(); | |
| 113 } else { | |
| 114 Log.w(TAG, "NFC is not supported."); | |
| 115 mNfcAdapter = null; | |
| 116 } | |
| 117 } else { | |
| 118 Log.w(TAG, "NFC operations are not permitted."); | |
| 119 mNfcAdapter = null; | |
| 120 mNfcManager = null; | |
| 121 mActivity = null; | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 /** | |
| 126 * Sets NfcClient. NfcClient interface is used to notify mojo NFC service | |
| 127 * client when NFC device is in proximity and has NfcMessage that matches | |
| 128 * NfcWatchOptions criteria. | |
| 129 * @see Nfc#watch(NfcWatchOptions options, WatchResponse callback) | |
| 130 * | |
| 131 * @param client @see NfcClient | |
| 132 */ | |
| 133 @Override | |
| 134 public void setClient(NfcClient client) { | |
| 135 // todo(shalamov): Should be implemented when watch() is implemented. | |
|
Ted C
2016/04/28 17:27:21
nit, TODO should be capitalized
shalamov
2016/05/11 14:09:57
Done.
| |
| 136 } | |
| 137 | |
| 138 /** | |
| 139 * Pushes NfcMessage to Tag or Peer, whenever NFC device is in proximity. | |
| 140 * At the moment, only passive NFC devices are supported (NfcPushTarget.TAG) . | |
| 141 * | |
| 142 * @param message that should be pushed to NFC device. | |
| 143 * @param options that contain information about timeout and target device t ype. | |
| 144 * @param callback that is used to notify when push operation is completed. | |
| 145 */ | |
| 146 @Override | |
| 147 public void push(NfcMessage message, NfcPushOptions options, PushResponse ca llback) { | |
| 148 if (!checkIfReady(callback)) return; | |
| 149 | |
| 150 if (options.target == NfcPushTarget.PEER) { | |
| 151 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); | |
| 152 return; | |
| 153 } | |
| 154 | |
| 155 // If previous pending push operation is not completed, subsequent call | |
| 156 // should cancel pending operation. | |
| 157 if (mPendingPushOperation != null) { | |
| 158 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED)); | |
| 159 } | |
| 160 | |
| 161 mPendingPushOperation = new PendingPushOperation(message, options, callb ack); | |
| 162 enableReaderMode(); | |
|
Ted C
2016/04/28 17:27:22
as far as I can tell, push only works if Android i
| |
| 163 processPendingPushOperation(); | |
| 164 } | |
| 165 | |
| 166 /** | |
| 167 * Cancels pending push operation. | |
| 168 * At the moment, only passive NFC devices are supported (NfcPushTarget.TAG) . | |
| 169 * | |
| 170 * @param target @see NfcPushTarget | |
| 171 * @param callback that is used to notify caller when cancelPush() is comple ted. | |
| 172 */ | |
| 173 @Override | |
| 174 public void cancelPush(int target, CancelPushResponse callback) { | |
| 175 if (!checkIfReady(callback)) return; | |
| 176 | |
| 177 if (target == NfcPushTarget.PEER) { | |
| 178 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); | |
| 179 return; | |
| 180 } | |
| 181 | |
| 182 if (mPendingPushOperation != null) { | |
| 183 mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CA NCELLED)); | |
| 184 mPendingPushOperation = null; | |
| 185 callback.call(null); | |
| 186 disableReaderMode(); | |
| 187 } else { | |
| 188 callback.call(createError(NfcErrorType.NOT_FOUND)); | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 /** | |
| 193 * Watch method allows to set filtering criteria for NfcMessages that are | |
| 194 * found when NFC device is within proximity. On success, watch ID is | |
| 195 * returned to caller through WatchResponse callback. When NfcMessage that | |
| 196 * matches NfcWatchOptions is found, it is passed to NfcClient interface | |
| 197 * together with corresponding watch ID. | |
| 198 * @see NfcClient#onWatch(int id, NfcMessage message) | |
| 199 * | |
| 200 * @param options used to filter NfcMessages, @see NfcWatchOptions. | |
| 201 * @param callback that is used to notify caller when watch() is completed a nd return watch ID. | |
| 202 */ | |
| 203 @Override | |
| 204 public void watch(NfcWatchOptions options, WatchResponse callback) { | |
| 205 if (!checkIfReady(callback)) return; | |
| 206 // todo(shalamov): Not implemented. | |
| 207 callback.call(0, createError(NfcErrorType.NOT_SUPPORTED)); | |
| 208 } | |
| 209 | |
| 210 /** | |
| 211 * Cancels NFC watch operation. | |
| 212 * | |
| 213 * @param id of watch operation. | |
| 214 * @param callback that is used to notify caller when cancelWatch() is compl eted. | |
| 215 */ | |
| 216 @Override | |
| 217 public void cancelWatch(int id, CancelWatchResponse callback) { | |
| 218 if (!checkIfReady(callback)) return; | |
| 219 // todo(shalamov): Not implemented. | |
| 220 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); | |
| 221 } | |
| 222 | |
| 223 /** | |
| 224 * Cancels all NFC watch operations. | |
| 225 * | |
| 226 * @param callback that is used to notify caller when cancelAllWatches() is completed. | |
| 227 */ | |
| 228 @Override | |
| 229 public void cancelAllWatches(CancelAllWatchesResponse callback) { | |
| 230 if (!checkIfReady(callback)) return; | |
| 231 // todo(shalamov): Not implemented. | |
| 232 callback.call(createError(NfcErrorType.NOT_SUPPORTED)); | |
| 233 } | |
| 234 | |
| 235 /** | |
| 236 * Suspends all pending watch / push operations. Should be called when web | |
| 237 * page visibility is lost. | |
| 238 */ | |
| 239 @Override | |
| 240 public void suspendNfcOperations() { | |
| 241 // todo(shalamov): Not implemented. | |
| 242 } | |
| 243 | |
| 244 /** | |
| 245 * Resumes all pending watch / push operations. Should be called when web | |
| 246 * page becomes visible. | |
| 247 */ | |
| 248 @Override | |
| 249 public void resumeNfcOperations() { | |
| 250 // todo(shalamov): Not implemented. | |
| 251 } | |
| 252 | |
| 253 @Override | |
| 254 public void close() {} | |
|
Ted C
2016/04/28 17:27:22
what close is this associated with?
To me, it loo
shalamov
2016/05/11 14:09:57
Done.
| |
| 255 | |
| 256 @Override | |
| 257 public void onConnectionError(MojoException e) {} | |
| 258 | |
| 259 /** | |
| 260 * Holds information about pending push operation. | |
| 261 */ | |
| 262 private static class PendingPushOperation { | |
| 263 private final NfcMessage mNfcMessage; | |
| 264 private final NfcPushOptions mNfcPushOptions; | |
| 265 private final PushResponse mPushResponseCallback; | |
| 266 | |
| 267 public PendingPushOperation( | |
| 268 NfcMessage message, NfcPushOptions options, PushResponse callbac k) { | |
| 269 mNfcMessage = message; | |
| 270 mNfcPushOptions = options; | |
| 271 mPushResponseCallback = callback; | |
| 272 } | |
| 273 | |
| 274 /** | |
| 275 * Completes pending push operation. | |
| 276 * | |
| 277 * @param error should be null when operation is completed successfully, | |
| 278 * otherwise, error object with corresponding NfcErrorType must be provi ded. | |
|
Ted C
2016/04/28 17:27:22
align w/ should above
shalamov
2016/05/11 14:09:57
Done.
| |
| 279 */ | |
| 280 public void complete(NfcError error) { | |
| 281 if (mPushResponseCallback != null) mPushResponseCallback.call(error) ; | |
| 282 } | |
| 283 | |
| 284 public NfcMessage message() { | |
| 285 return mNfcMessage; | |
| 286 } | |
| 287 public NfcPushOptions pushOptions() { | |
|
Ted C
2016/04/28 17:27:22
add a blank line above this.
Also, you could make
shalamov
2016/05/11 14:09:57
Done.
| |
| 288 return mNfcPushOptions; | |
| 289 } | |
| 290 } | |
| 291 | |
| 292 /** | |
| 293 * Helper method that creates NfcError object from NfcErrorType. | |
| 294 * | |
| 295 * @param errorType @see NfcErrorType. | |
| 296 * @return NfcError | |
| 297 * @see NfcError | |
| 298 */ | |
| 299 private NfcError createError(int errorType) { | |
| 300 NfcError error = new NfcError(); | |
| 301 error.errorType = errorType; | |
| 302 return error; | |
| 303 } | |
| 304 | |
| 305 /** | |
| 306 * Checks if NFC funcionality can be used by the mojo service. | |
| 307 * If permission to use NFC is granted and hardware is enabled, returns null . | |
| 308 * | |
| 309 * @return NfcError | |
| 310 */ | |
| 311 @Nullable | |
| 312 private NfcError checkIfReady() { | |
| 313 if (!mHasPermission) { | |
| 314 return createError(NfcErrorType.SECURITY); | |
| 315 } else if (mNfcManager == null || mNfcAdapter == null | |
| 316 || Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { | |
|
Ted C
2016/04/28 17:27:21
I would still not create the mNfcManager for KK.
shalamov
2016/05/11 14:09:57
Done.
| |
| 317 return createError(NfcErrorType.NOT_SUPPORTED); | |
| 318 } else if (!mNfcAdapter.isEnabled()) { | |
| 319 return createError(NfcErrorType.DEVICE_DISABLED); | |
| 320 } | |
| 321 return null; | |
| 322 } | |
| 323 | |
| 324 /** | |
| 325 * Uses checkIfReady() method and if NFC functionality cannot be used, | |
| 326 * calls mojo callback with NfcError. | |
| 327 * | |
| 328 * @param WatchResponse Callback that is provided to watch() method. | |
| 329 * @return boolean true if NFC functionality can be used, false otherwise. | |
| 330 */ | |
| 331 private boolean checkIfReady(WatchResponse callback) { | |
| 332 NfcError error = checkIfReady(); | |
| 333 if (error == null) return true; | |
| 334 callback.call(0, error); | |
| 335 return false; | |
| 336 } | |
| 337 | |
| 338 /** | |
| 339 * Uses checkIfReady() method and if NFC functionality cannot be used, | |
| 340 * calls mojo callback NfcError. | |
| 341 * | |
| 342 * @param callback Generic callback that is provided to push(), cancelPush() , | |
| 343 * cancelWatch() and cancelAllWatches() methods. | |
| 344 * @return boolean true if NFC functionality can be used, false otherwise. | |
| 345 */ | |
| 346 private boolean checkIfReady(Callbacks.Callback1<NfcError> callback) { | |
| 347 NfcError error = checkIfReady(); | |
| 348 if (error == null) return true; | |
| 349 callback.call(error); | |
| 350 return false; | |
| 351 } | |
| 352 | |
| 353 /** | |
| 354 * Implementation of android.nfc.NfcAdapter.ReaderCallback. | |
| 355 * Callback is called when NFC tag is discovered, Tag object is delegated | |
| 356 * to mojo service implementation method NfcImpl.onTagDiscovered(). | |
| 357 */ | |
| 358 @TargetApi(Build.VERSION_CODES.KITKAT) | |
| 359 private static class ReaderCallbackHandler implements ReaderCallback { | |
| 360 private final NfcImpl mNfcImpl; | |
| 361 | |
| 362 public ReaderCallbackHandler(NfcImpl impl) { | |
| 363 mNfcImpl = impl; | |
| 364 } | |
| 365 | |
| 366 @Override | |
| 367 public void onTagDiscovered(Tag tag) { | |
| 368 mNfcImpl.onTagDiscovered(tag); | |
| 369 } | |
| 370 } | |
| 371 | |
| 372 /** | |
| 373 * Enables reader mode, allowing NFC device to read / write NFC tags. | |
| 374 * @see android.nfc.NfcAdapter#enableReaderMode | |
| 375 */ | |
| 376 private void enableReaderMode() { | |
| 377 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; | |
| 378 if (mReaderCallbackHandler != null) return; | |
| 379 | |
| 380 mReaderCallbackHandler = new ReaderCallbackHandler(this); | |
| 381 mNfcAdapter.enableReaderMode(mActivity, mReaderCallbackHandler, | |
| 382 NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B | |
| 383 | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG_READER_ NFC_V, | |
| 384 null); | |
| 385 } | |
| 386 | |
| 387 /** | |
| 388 * Disables reader mode. | |
| 389 * @see android.nfc.NfcAdapter#disableReaderMode | |
| 390 */ | |
| 391 private void disableReaderMode() { | |
| 392 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; | |
| 393 | |
| 394 mReaderCallbackHandler = null; | |
| 395 mNfcAdapter.disableReaderMode(mActivity); | |
| 396 } | |
| 397 | |
| 398 /** | |
| 399 * NdefFormatable and Ndef interfaces have different signatures for writing | |
| 400 * NdefMessage to a tag. This interface provides generic write method. | |
| 401 */ | |
| 402 private interface TagTechnologyWriter { | |
| 403 public void write(NdefMessage message) | |
| 404 throws IOException, TagLostException, FormatException; | |
| 405 } | |
| 406 | |
| 407 /** | |
| 408 * Implementation of TagTechnologyWriter that can write NdefMessage to NFC t ag. | |
| 409 */ | |
| 410 private static class NdefWriter implements TagTechnologyWriter { | |
| 411 private final Ndef mNdef; | |
| 412 | |
| 413 NdefWriter(Ndef ndef) { | |
| 414 mNdef = ndef; | |
| 415 } | |
| 416 | |
| 417 public void write(NdefMessage message) | |
| 418 throws IOException, TagLostException, FormatException { | |
| 419 mNdef.writeNdefMessage(message); | |
| 420 } | |
| 421 } | |
| 422 | |
| 423 /** | |
| 424 * Implementation of TagTechnologyWriter that can format empty NFC tag | |
| 425 * with provided NFCMessage. | |
| 426 */ | |
| 427 private static class NdefFormattableWriter implements TagTechnologyWriter { | |
| 428 private final NdefFormatable mNdefFormattable; | |
| 429 | |
| 430 NdefFormattableWriter(NdefFormatable ndefFormattable) { | |
| 431 mNdefFormattable = ndefFormattable; | |
| 432 } | |
| 433 | |
| 434 public void write(NdefMessage message) | |
| 435 throws IOException, TagLostException, FormatException { | |
| 436 mNdefFormattable.format(message); | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 /** | |
| 441 * Utility class that holds TagTechnology and TagTechnologyWriter objects. | |
| 442 * Provides connectivity and I/O related operations for NFC tag. | |
| 443 */ | |
| 444 private static class NfcTagWriter { | |
| 445 private final TagTechnology mTech; | |
| 446 private final TagTechnologyWriter mTechWriter; | |
| 447 private boolean mWasConnected = false; | |
|
Ted C
2016/04/28 17:27:22
false is the default, so you can drop this
shalamov
2016/05/11 14:09:58
Done.
| |
| 448 | |
| 449 /** | |
| 450 * Factory method that creates NfcTagWriter with TagTechnologyWriter | |
| 451 * appropriate for a given NFC Tag. | |
| 452 * | |
| 453 * @param tag @see android.nfc.Tag | |
| 454 * @return NfcTagWriter or null when unsupported Tag is provided. | |
| 455 */ | |
| 456 public static NfcTagWriter create(Tag tag) { | |
| 457 if (tag == null) return null; | |
| 458 | |
| 459 Ndef ndef = Ndef.get(tag); | |
| 460 if (ndef != null) return new NfcTagWriter(ndef, new NdefWriter(ndef) ); | |
| 461 | |
| 462 NdefFormatable formattable = NdefFormatable.get(tag); | |
| 463 if (formattable != null) { | |
| 464 return new NfcTagWriter(formattable, new NdefFormattableWriter(f ormattable)); | |
| 465 } | |
| 466 | |
| 467 return null; | |
| 468 } | |
| 469 | |
| 470 private NfcTagWriter(TagTechnology tech, TagTechnologyWriter writer) { | |
| 471 mTech = tech; | |
| 472 mTechWriter = writer; | |
| 473 } | |
| 474 | |
| 475 /** | |
| 476 * Connects to NFC tag. | |
| 477 */ | |
| 478 public void connect() throws IOException, TagLostException { | |
| 479 if (!mTech.isConnected()) { | |
| 480 mTech.connect(); | |
| 481 mWasConnected = true; | |
| 482 } | |
| 483 } | |
| 484 | |
| 485 /** | |
| 486 * Closes connection. | |
| 487 */ | |
| 488 public void close() throws IOException { | |
| 489 mTech.close(); | |
| 490 } | |
| 491 | |
| 492 /** | |
| 493 * Writes NdefMessage to NFC tag. | |
| 494 * | |
| 495 * @param message @see android.nfc.NdefMessage | |
| 496 */ | |
| 497 public void write(NdefMessage message) | |
| 498 throws IOException, TagLostException, FormatException { | |
| 499 mTechWriter.write(message); | |
| 500 } | |
| 501 | |
| 502 /** | |
| 503 * If tag was previously connected and subsequent connection to the same | |
| 504 * tag fails, consider tag to be out of ragne. | |
| 505 */ | |
| 506 public boolean isTagOutOfRange() { | |
| 507 try { | |
| 508 connect(); | |
| 509 } catch (IOException e) { | |
| 510 return mWasConnected; | |
| 511 } | |
| 512 return false; | |
| 513 } | |
| 514 } | |
| 515 | |
| 516 /** | |
| 517 * Exception that is raised when mojo NfcMessage cannot be coverted to NdefM essage. | |
| 518 */ | |
| 519 private static class InvalidMessageException extends Exception {} | |
| 520 | |
| 521 /** | |
| 522 * Converts mojo NfcMessage to android.nfc.NdefMessage. | |
| 523 * | |
| 524 * @param message mojo NfcMessage | |
| 525 * @return NdefMessage | |
| 526 * @see android.nfc.NdefMessage | |
| 527 */ | |
| 528 private NdefMessage toNdefMessage(NfcMessage message) throws InvalidMessageE xception { | |
| 529 if (message == null || message.data.length == 0) throw new InvalidMessag eException(); | |
| 530 | |
| 531 try { | |
| 532 List<NdefRecord> records = new ArrayList<NdefRecord>(); | |
| 533 for (NfcRecord record : message.data) { | |
|
Ted C
2016/04/28 17:27:21
is message.data an array primitive? In general, t
shalamov
2016/05/11 14:09:57
Done.
| |
| 534 records.add(toNdefRecord(record)); | |
| 535 } | |
| 536 records.add(NdefRecord.createExternal(DOMAIN, TYPE, message.url.getB ytes())); | |
| 537 NdefRecord[] ndefRecords = new NdefRecord[records.size()]; | |
| 538 records.toArray(ndefRecords); | |
| 539 return new NdefMessage(ndefRecords); | |
| 540 } catch (UnsupportedEncodingException | InvalidMessageException | |
| 541 | IllegalArgumentException e) { | |
| 542 throw new InvalidMessageException(); | |
| 543 } | |
| 544 } | |
| 545 | |
| 546 /** | |
| 547 * Returns charset of mojo NfcRecord. Only applicable for URL and TEXT recor ds. | |
| 548 * If charset cannot be determined, UTF-8 charset is used by default. | |
| 549 * | |
| 550 * @param record | |
|
Ted C
2016/04/28 17:27:21
these are a bit terse...you can probably drop thes
shalamov
2016/05/11 14:09:57
Done.
| |
| 551 * @return String | |
| 552 */ | |
| 553 private String getCharset(NfcRecord record) { | |
| 554 if (record.mediaType.endsWith(CHARSET_UTF8)) return "UTF-8"; | |
| 555 | |
| 556 if (record.mediaType.endsWith(CHARSET_UTF16)) return "UTF-16LE"; | |
| 557 | |
| 558 Log.w(TAG, "Unknown charset, defaulting to UTF-8."); | |
| 559 return "UTF-8"; | |
| 560 } | |
| 561 | |
| 562 /** | |
| 563 * Converts mojo NfcRecord to android.nfc.NdefRecord. | |
| 564 * | |
| 565 * @param record mojo NfcRecord | |
| 566 * @return NdefRecord | |
| 567 * @see android.nfc.NdefRecord | |
| 568 */ | |
| 569 private NdefRecord toNdefRecord(NfcRecord record) | |
| 570 throws InvalidMessageException, IllegalArgumentException, Unsupporte dEncodingException { | |
| 571 switch (record.recordType) { | |
| 572 case NfcRecordType.URL: | |
| 573 return NdefRecord.createUri(new String(record.data, getCharset(r ecord))); | |
| 574 case NfcRecordType.TEXT: | |
| 575 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | |
| 576 return NdefRecord.createTextRecord( | |
| 577 "en-US", new String(record.data, getCharset(record)) ); | |
| 578 } else { | |
| 579 return NdefRecord.createMime(TEXT_MIME, record.data); | |
| 580 } | |
| 581 case NfcRecordType.JSON: | |
| 582 case NfcRecordType.OPAQUE_RECORD: | |
| 583 return NdefRecord.createMime(record.mediaType, record.data); | |
| 584 default: | |
| 585 throw new InvalidMessageException(); | |
| 586 } | |
| 587 } | |
| 588 | |
| 589 /** | |
| 590 * Completes pending push operation. On error, invalidates #mTagWriter. | |
| 591 * | |
| 592 * @param error | |
|
Ted C
2016/04/28 17:27:22
same...you can drop all of these empty javadoc par
shalamov
2016/05/11 14:09:58
Done.
| |
| 593 */ | |
| 594 private void pendingPushOperationCompleted(NfcError error) { | |
| 595 if (mPendingPushOperation != null) { | |
| 596 mPendingPushOperation.complete(error); | |
| 597 mPendingPushOperation = null; | |
| 598 } | |
| 599 | |
| 600 if (error != null) mTagWriter = null; | |
| 601 } | |
| 602 | |
| 603 /** | |
| 604 * Checks whether there is a #mPendingPushOperation and writes data to NFC t ag. | |
| 605 * In case of exception calls pendingPushOperationCompleted() with appropria te | |
| 606 * error object. | |
| 607 */ | |
| 608 private void processPendingPushOperation() { | |
| 609 if (mTagWriter == null || mPendingPushOperation == null) return; | |
| 610 | |
| 611 if (mTagWriter.isTagOutOfRange()) { | |
| 612 mTagWriter = null; | |
| 613 return; | |
| 614 } | |
| 615 | |
| 616 try { | |
| 617 mTagWriter.connect(); | |
| 618 mTagWriter.write(toNdefMessage(mPendingPushOperation.message())); | |
| 619 pendingPushOperationCompleted(null); | |
| 620 mTagWriter.close(); | |
| 621 } catch (InvalidMessageException e) { | |
| 622 Log.w(TAG, "Cannot write data to NFC tag. Invalid NfcMessage."); | |
| 623 pendingPushOperationCompleted(createError(NfcErrorType.INVALID_MESSA GE)); | |
| 624 } catch (TagLostException e) { | |
| 625 Log.w(TAG, "Cannot write data to NFC tag. Tag is lost."); | |
| 626 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR)); | |
| 627 } catch (FormatException | IOException e) { | |
| 628 Log.w(TAG, "Cannot write data to NFC tag. IO_ERROR."); | |
| 629 pendingPushOperationCompleted(createError(NfcErrorType.IO_ERROR)); | |
| 630 } | |
| 631 } | |
| 632 | |
| 633 /** | |
| 634 * Called by ReaderCallbackHandler when NFC tag is in proximity. | |
| 635 * calls processPendingPushOperation() that will write data to a tag. | |
| 636 * | |
| 637 * @param tag | |
| 638 */ | |
| 639 public void onTagDiscovered(Tag tag) { | |
| 640 mTagWriter = NfcTagWriter.create(tag); | |
| 641 processPendingPushOperation(); | |
| 642 } | |
| 643 } | |
| OLD | NEW |