Adding new samples to browseable section of DAC
Change-Id: I58e10e787f5df668331fc04e97a6c2efcd75f76f
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.example.android.cardreader;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.example.android.common.logger.Log;
|
||||
|
||||
/**
|
||||
* Generic UI for sample discovery.
|
||||
*/
|
||||
public class CardReaderFragment extends Fragment implements LoyaltyCardReader.AccountCallback {
|
||||
|
||||
public static final String TAG = "CardReaderFragment";
|
||||
// Recommend NfcAdapter flags for reading from other Android devices. Indicates that this
|
||||
// activity is interested in NFC-A devices (including other Android devices), and that the
|
||||
// system should not check for the presence of NDEF-formatted data (e.g. Android Beam).
|
||||
public static int READER_FLAGS =
|
||||
NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK;
|
||||
public LoyaltyCardReader mLoyaltyCardReader;
|
||||
private TextView mAccountField;
|
||||
|
||||
/** Called when sample is created. Displays generic UI with welcome text. */
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
// Inflate the layout for this fragment
|
||||
View v = inflater.inflate(R.layout.main_fragment, container, false);
|
||||
if (v != null) {
|
||||
mAccountField = (TextView) v.findViewById(R.id.card_account_field);
|
||||
mAccountField.setText("Waiting...");
|
||||
|
||||
mLoyaltyCardReader = new LoyaltyCardReader(this);
|
||||
|
||||
// Disable Android Beam and register our card reader callback
|
||||
enableReaderMode();
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
disableReaderMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
enableReaderMode();
|
||||
}
|
||||
|
||||
private void enableReaderMode() {
|
||||
Log.i(TAG, "Enabling reader mode");
|
||||
Activity activity = getActivity();
|
||||
NfcAdapter nfc = NfcAdapter.getDefaultAdapter(activity);
|
||||
if (nfc != null) {
|
||||
nfc.enableReaderMode(activity, mLoyaltyCardReader, READER_FLAGS, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void disableReaderMode() {
|
||||
Log.i(TAG, "Disabling reader mode");
|
||||
Activity activity = getActivity();
|
||||
NfcAdapter nfc = NfcAdapter.getDefaultAdapter(activity);
|
||||
if (nfc != null) {
|
||||
nfc.disableReaderMode(activity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccountReceived(final String account) {
|
||||
// This callback is run on a background thread, but updates to UI elements must be performed
|
||||
// on the UI thread.
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mAccountField.setText(account);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.example.android.cardreader;
|
||||
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.nfc.Tag;
|
||||
import android.nfc.tech.IsoDep;
|
||||
|
||||
import com.example.android.common.logger.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Callback class, invoked when an NFC card is scanned while the device is running in reader mode.
|
||||
*
|
||||
* Reader mode can be invoked by calling NfcAdapter
|
||||
*/
|
||||
public class LoyaltyCardReader implements NfcAdapter.ReaderCallback {
|
||||
private static final String TAG = "LoyaltyCardReader";
|
||||
// AID for our loyalty card service.
|
||||
private static final String SAMPLE_LOYALTY_CARD_AID = "F222222222";
|
||||
// ISO-DEP command HEADER for selecting an AID.
|
||||
// Format: [Class | Instruction | Parameter 1 | Parameter 2]
|
||||
private static final String SELECT_APDU_HEADER = "00A40400";
|
||||
// "OK" status word sent in response to SELECT AID command (0x9000)
|
||||
private static final byte[] SELECT_OK_SW = {(byte) 0x90, (byte) 0x00};
|
||||
|
||||
// Weak reference to prevent retain loop. mAccountCallback is responsible for exiting
|
||||
// foreground mode before it becomes invalid (e.g. during onPause() or onStop()).
|
||||
private WeakReference<AccountCallback> mAccountCallback;
|
||||
|
||||
public interface AccountCallback {
|
||||
public void onAccountReceived(String account);
|
||||
}
|
||||
|
||||
public LoyaltyCardReader(AccountCallback accountCallback) {
|
||||
mAccountCallback = new WeakReference<AccountCallback>(accountCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when a new tag is discovered by the system.
|
||||
*
|
||||
* <p>Communication with the card should take place here.
|
||||
*
|
||||
* @param tag Discovered tag
|
||||
*/
|
||||
@Override
|
||||
public void onTagDiscovered(Tag tag) {
|
||||
Log.i(TAG, "New tag discovered");
|
||||
// Android's Host-based Card Emulation (HCE) feature implements the ISO-DEP (ISO 14443-4)
|
||||
// protocol.
|
||||
//
|
||||
// In order to communicate with a device using HCE, the discovered tag should be processed
|
||||
// using the IsoDep class.
|
||||
IsoDep isoDep = IsoDep.get(tag);
|
||||
if (isoDep != null) {
|
||||
try {
|
||||
// Connect to the remote NFC device
|
||||
isoDep.connect();
|
||||
// Build SELECT AID command for our loyalty card service.
|
||||
// This command tells the remote device which service we wish to communicate with.
|
||||
Log.i(TAG, "Requesting remote AID: " + SAMPLE_LOYALTY_CARD_AID);
|
||||
byte[] command = BuildSelectApdu(SAMPLE_LOYALTY_CARD_AID);
|
||||
// Send command to remote device
|
||||
Log.i(TAG, "Sending: " + ByteArrayToHexString(command));
|
||||
byte[] result = isoDep.transceive(command);
|
||||
// If AID is successfully selected, 0x9000 is returned as the status word (last 2
|
||||
// bytes of the result) by convention. Everything before the status word is
|
||||
// optional payload, which is used here to hold the account number.
|
||||
int resultLength = result.length;
|
||||
byte[] statusWord = {result[resultLength-2], result[resultLength-1]};
|
||||
byte[] payload = Arrays.copyOf(result, resultLength-2);
|
||||
if (Arrays.equals(SELECT_OK_SW, statusWord)) {
|
||||
// The remote NFC device will immediately respond with its stored account number
|
||||
String accountNumber = new String(payload, "UTF-8");
|
||||
Log.i(TAG, "Received: " + accountNumber);
|
||||
// Inform CardReaderFragment of received account number
|
||||
mAccountCallback.get().onAccountReceived(accountNumber);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error communicating with card: " + e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build APDU for SELECT AID command. This command indicates which service a reader is
|
||||
* interested in communicating with. See ISO 7816-4.
|
||||
*
|
||||
* @param aid Application ID (AID) to select
|
||||
* @return APDU for SELECT AID command
|
||||
*/
|
||||
public static byte[] BuildSelectApdu(String aid) {
|
||||
// Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA]
|
||||
return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02X", aid.length() / 2) + aid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class to convert a byte array to a hexadecimal string.
|
||||
*
|
||||
* @param bytes Bytes to convert
|
||||
* @return String, containing hexadecimal representation.
|
||||
*/
|
||||
public static String ByteArrayToHexString(byte[] bytes) {
|
||||
final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
|
||||
char[] hexChars = new char[bytes.length * 2];
|
||||
int v;
|
||||
for ( int j = 0; j < bytes.length; j++ ) {
|
||||
v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = hexArray[v >>> 4];
|
||||
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
|
||||
}
|
||||
return new String(hexChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class to convert a hexadecimal string to a byte string.
|
||||
*
|
||||
* <p>Behavior with input strings containing non-hexadecimal characters is undefined.
|
||||
*
|
||||
* @param s String containing hexadecimal characters to convert
|
||||
* @return Byte array generated from input
|
||||
*/
|
||||
public static byte[] HexStringToByteArray(String s) {
|
||||
int len = s.length();
|
||||
byte[] data = new byte[len / 2];
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
|
||||
+ Character.digit(s.charAt(i+1), 16));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
package com.example.android.cardreader;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.ViewAnimator;
|
||||
|
||||
import com.example.android.common.activities.SampleActivityBase;
|
||||
import com.example.android.common.logger.Log;
|
||||
import com.example.android.common.logger.LogFragment;
|
||||
import com.example.android.common.logger.LogWrapper;
|
||||
import com.example.android.common.logger.MessageOnlyLogFilter;
|
||||
|
||||
/**
|
||||
* A simple launcher activity containing a summary sample description, sample log and a custom
|
||||
* {@link android.support.v4.app.Fragment} which can display a view.
|
||||
* <p>
|
||||
* For devices with displays with a width of 720dp or greater, the sample log is always visible,
|
||||
* on other devices it's visibility is controlled by an item on the Action Bar.
|
||||
*/
|
||||
public class MainActivity extends SampleActivityBase {
|
||||
|
||||
public static final String TAG = "MainActivity";
|
||||
|
||||
// Whether the Log Fragment is currently shown
|
||||
private boolean mLogShown;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
||||
CardReaderFragment fragment = new CardReaderFragment();
|
||||
transaction.replace(R.id.sample_content_fragment, fragment);
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.main, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
|
||||
logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
|
||||
logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
|
||||
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch(item.getItemId()) {
|
||||
case R.id.menu_toggle_log:
|
||||
mLogShown = !mLogShown;
|
||||
ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
|
||||
if (mLogShown) {
|
||||
output.setDisplayedChild(1);
|
||||
} else {
|
||||
output.setDisplayedChild(0);
|
||||
}
|
||||
supportInvalidateOptionsMenu();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/** Create a chain of targets that will receive log data */
|
||||
@Override
|
||||
public void initializeLogging() {
|
||||
// Wraps Android's native log framework.
|
||||
LogWrapper logWrapper = new LogWrapper();
|
||||
// Using Log, front-end to the logging chain, emulates android.util.log method signatures.
|
||||
Log.setLogNode(logWrapper);
|
||||
|
||||
// Filter strips out everything except the message text.
|
||||
MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
|
||||
logWrapper.setNext(msgFilter);
|
||||
|
||||
// On screen logging via a fragment with a TextView.
|
||||
LogFragment logFragment = (LogFragment) getSupportFragmentManager()
|
||||
.findFragmentById(R.id.log_fragment);
|
||||
msgFilter.setNext(logFragment.getLogView());
|
||||
|
||||
Log.i(TAG, "Ready");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user