آموزش خواندن و نوشتن NFC Tag در برنامه نویسی اندروید

سلام دوستان امیدوارم حالتان خوب باشد در این سری از آموزش برنامه نویسی اندروید به آموزش خواندن و نوشتن NFC Tag در برنامه نویسی اندروید می پردازیم قبلتر آموزشی کلی در این رابطه قرار داده بودیم که به صورت کلی Nfc Tag را مورد تشریح و استفاده قرار دادیم در این آموزش به خواندن و نوشتن Nfc Tag خواهیم پرداخت در ادمه با ما همراه باشید.
موارد مورد استفاده در Nfc Tag چیست ؟
به طور کلی برای نوشتن و دریافت مقدار دادی بسیار کم مثل url یا اعداد
برای اجرای برنامه ی خاص
برای باز کردن قفل (مورد استفاده در مترو)
یک کلید امنیتی برای یک اپلیکیشن
ابتدا Nfc گوشی خود را روشن کنید !
برای کار کردن با Nfc لازم است تا دسترسی آن را به AndroidManifest.xml اضافه کنید.

<uses-permission android:name="android.permission.NFC" />

باید وارد فایل Build.gradle شده و آن را به شکل زیر تغییر دهید چون از متودهای Lambda جاوا 8 استفاده می کنیم باید حتما تغییر کند.
جاهایی که باید اضافه کنید با تگ here مشخص شده است.

apply plugin: 'com.android.application'
android {
    compileSdkVersion 25
    buildToolsVersion "25.0.0"
    defaultConfig {
        applicationId "ir.programchi"
        minSdkVersion 19
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        //here
        jackOptions {
            enabled true
        }
    }
    //here
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.0.0'
    testCompile 'junit:junit:4.12'
}

بروی دکمه sync کلیک کنید.
وارد فایل String.xml شده و کدهای زیر را قرار دهید.

<resources>
    <string name="app_name">NFC</string>
    <string name="message_write_error">Error Writing Message !</string>
    <string name="message_write_success">Successfully written to Tag !</string>
    <string name="message_write_progress">Writing to Tag..</string>
    <string name="message_tag_detected">NFC Tag Detected !</string>
    <string name="message_read_tag">Read from NFC Tag</string>
    <string name="message_write_tag">Write to NFC Tag</string>
    <string name="message_tap_tag">Tap the NFC Tag to your device !</string>
    <string name="hint_message">Message</string>
</resources>

نام های بالا در ادامه برنامه استفاده خواهند شد.
در بخش layout دو دکمه و یک Edittext برای ورود داده قرار دهید همانند زیر

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:gravity="center"
    tools:context="ir.programchi.nfc.MainActivity">
    <EditText
        android:id="@+id/et_message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/hint_message"/>
    <Button
        android:id="@+id/btn_write"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/et_message"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:text="@string/message_write_tag"
        style="@style/Widget.AppCompat.Button.Colored"/>
    <Button
        android:id="@+id/btn_read"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_write"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:text="@string/message_read_tag"
        style="@style/Widget.AppCompat.Button.Colored"/>
</RelativeLayout>

در ادامه ما یک فرگمنت داریم که اطلاعات خوانده شده در آن نمایش داده می شود بخش layout آن همانند زیر است نام آن برابر با fragment_read.xml است.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/activity_horizontal_margin">
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/message_read_tag"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_gravity="center"/>
    <ImageView
        android:id="@+id/logo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:src="@drawable/ic_nfc"
        android:tint="@color/colorAccent"
        android:layout_gravity="center"/>
    <TextView
        android:id="@+id/tv_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:layout_gravity="center"
        android:textAppearance="?android:attr/textAppearanceMedium"/>
</LinearLayout>

یک فرگمنت دیگر برای نوشتن به نام fragment_write.xml ایجاد کرده و کدهای زیر را در آن قرار دهید.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/activity_horizontal_margin">
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="@string/message_write_tag"
        android:textAppearance="?android:attr/textAppearanceLarge"/>
    <ImageView
        android:id="@+id/logo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:src="@drawable/ic_nfc"
        android:tint="@color/colorAccent"/>
    <ProgressBar
        android:id="@+id/progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:visibility="gone"/>
    <TextView
        android:id="@+id/tv_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:text="@string/message_tap_tag"/>
</LinearLayout>

 
حالا باید Interface درست کنیم این Interface ها بعدا مورد استفاده قرار می گیرند.
 

public interface Listener {
    void onDialogDisplayed();
    void onDialogDismissed();
}

مثل دوتا Event کار خواهند کرد.
کدهای مربوط به فایل NFCReadFragment که برای خواندن تگ Nfc مورد استفاده قرار میگیرد همانند زیر است.

package ir.programchi;
import android.app.DialogFragment;
import android.content.Context;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.io.IOException;
public class NFCReadFragment extends DialogFragment {
    public static final String TAG = NFCReadFragment.class.getSimpleName();
    public static NFCReadFragment newInstance() {
        return new NFCReadFragment();
    }
    private TextView mTvMessage;
    private Listener mListener;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_read,container,false);
        initViews(view);
        return view;
    }
    private void initViews(View view) {
        mTvMessage = (TextView) view.findViewById(R.id.tv_message);
    }
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mListener = (MainActivity)context;
        mListener.onDialogDisplayed();
    }
    @Override
    public void onDetach() {
        super.onDetach();
        mListener.onDialogDismissed();
    }
    public void onNfcDetected(Ndef ndef){
        readFromNFC(ndef);
    }
    private void readFromNFC(Ndef ndef) {
        try {
            ndef.connect();
            NdefMessage ndefMessage = ndef.getNdefMessage();
            String message = new String(ndefMessage.getRecords()[0].getPayload());
            Log.d(TAG, "readFromNFC: "+message);
            mTvMessage.setText(message);
            ndef.close();
        } catch (IOException | FormatException e) {
            e.printStackTrace();
        }
    }
}

یک متود به نام initViews داریم که View های ما رو find می کند. چون داریم از فرگمنت استفاده می کنیم در بخش onAttach (این بخش قبلا در بخش فرگمنت ها توضیح داده شده است) یک Instance از MainActivty در Listener میریزیم اگر یادتان باشد در Interface یک void به نام onDialogDisplayed که آن را صدا می زنیم (دو متود onDialogDisplayed و onDialogDismissed در ManiActivity تعریف شده اند و با صدا زدن آنها عملی انجام می شود ).
یک void دیگر به نام onNfcDetected وجود دارد که تشخیص میدهد Nfc موجود است یا نه (منظور تگ Nfc است) در داخل این void یک void دیگر به نام readFromNFC است که کار خواندن را انجام میدهد.
پس خواندن یک Tag Nfc همانند بالا شد.
یک فرگمنت به نام NFCWriteFragment.java وجود دارد که عمل نوشتن بروی تگ Nfc را انجام میدهد و همانند زیر است.

package ir.programchi;
import android.app.DialogFragment;
import android.content.Context;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.io.IOException;
import java.nio.charset.Charset;
public class NFCWriteFragment extends DialogFragment {
    public static final String TAG = NFCWriteFragment.class.getSimpleName();
    public static NFCWriteFragment newInstance() {
        return new NFCWriteFragment();
    }
    private TextView mTvMessage;
    private ProgressBar mProgress;
    private Listener mListener;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_write,container,false);
        initViews(view);
        return view;
    }
    private void initViews(View view) {
        mTvMessage = (TextView) view.findViewById(R.id.tv_message);
        mProgress = (ProgressBar) view.findViewById(R.id.progress);
    }
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mListener = (MainActivity)context;
        mListener.onDialogDisplayed();
    }
    @Override
    public void onDetach() {
        super.onDetach();
        mListener.onDialogDismissed();
    }
    public void onNfcDetected(Ndef ndef, String messageToWrite){
        mProgress.setVisibility(View.VISIBLE);
        writeToNfc(ndef,messageToWrite);
    }
    private void writeToNfc(Ndef ndef, String message){
        mTvMessage.setText(getString(R.string.message_write_progress));
        if (ndef != null) {
            try {
                ndef.connect();
                NdefRecord mimeRecord = NdefRecord.createMime("text/plain", message.getBytes(Charset.forName("US-ASCII")));
                ndef.writeNdefMessage(new NdefMessage(mimeRecord));
                ndef.close();
                mTvMessage.setText(getString(R.string.message_write_success));
            } catch (IOException | FormatException e) {
                e.printStackTrace();
                mTvMessage.setText(getString(R.string.message_write_error));
            } finally {
                mProgress.setVisibility(View.GONE);
            }
        }
    }
}

 
همانند قبل برخی بخش ها تکراری است فقط به بخش writeToNfc خواهیم پرداخت در اینجا این متود یک متن تست را به تگ nfc نسبت میدهد و در آن ذخیره می کند چون نمیشه خود فایل text رو یعنی String رو ذخیره کرد مقدار Ascii آن را دریافت و در Nfc تگ قرار خواهیم داد.
به همین راحتی
بخش اصلی برنامه یا MainActivity

package ir.programchi;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements Listener{
    public static final String TAG = MainActivity.class.getSimpleName();
    private EditText mEtMessage;
    private Button mBtWrite;
    private Button mBtRead;
    private NFCWriteFragment mNfcWriteFragment;
    private NFCReadFragment mNfcReadFragment;
    private boolean isDialogDisplayed = false;
    private boolean isWrite = false;
    private NfcAdapter mNfcAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
        initNFC();
    }
    private void initViews() {
        mEtMessage = (EditText) findViewById(R.id.et_message);
        mBtWrite = (Button) findViewById(R.id.btn_write);
        mBtRead = (Button) findViewById(R.id.btn_read);
        mBtWrite.setOnClickListener(view -> showWriteFragment());
        mBtRead.setOnClickListener(view -> showReadFragment());
    }
    private void initNFC(){
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
    }
    private void showWriteFragment() {
        isWrite = true;
        mNfcWriteFragment = (NFCWriteFragment) getFragmentManager().findFragmentByTag(NFCWriteFragment.TAG);
        if (mNfcWriteFragment == null) {
            mNfcWriteFragment = NFCWriteFragment.newInstance();
        }
        mNfcWriteFragment.show(getFragmentManager(),NFCWriteFragment.TAG);
    }
    private void showReadFragment() {
        mNfcReadFragment = (NFCReadFragment) getFragmentManager().findFragmentByTag(NFCReadFragment.TAG);
        if (mNfcReadFragment == null) {
            mNfcReadFragment = NFCReadFragment.newInstance();
        }
        mNfcReadFragment.show(getFragmentManager(),NFCReadFragment.TAG);
    }
    @Override
    public void onDialogDisplayed() {
        isDialogDisplayed = true;
    }
    @Override
    public void onDialogDismissed() {
        isDialogDisplayed = false;
        isWrite = false;
    }
    @Override
    protected void onResume() {
        super.onResume();
        IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
        IntentFilter ndefDetected = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
        IntentFilter techDetected = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
        IntentFilter[] nfcIntentFilter = new IntentFilter[]{techDetected,tagDetected,ndefDetected};
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
        if(mNfcAdapter!= null)
            mNfcAdapter.enableForegroundDispatch(this, pendingIntent, nfcIntentFilter, null);
    }
    @Override
    protected void onPause() {
        super.onPause();
        if(mNfcAdapter!= null)
            mNfcAdapter.disableForegroundDispatch(this);
    }
    @Override
    protected void onNewIntent(Intent intent) {
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        Log.d(TAG, "onNewIntent: "+intent.getAction());
        if(tag != null) {
            Toast.makeText(this, getString(R.string.message_tag_detected), Toast.LENGTH_SHORT).show();
            Ndef ndef = Ndef.get(tag);
            if (isDialogDisplayed) {
                if (isWrite) {
                    String messageToWrite = mEtMessage.getText().toString();
                    mNfcWriteFragment = (NFCWriteFragment) getFragmentManager().findFragmentByTag(NFCWriteFragment.TAG);
                    mNfcWriteFragment.onNfcDetected(ndef,messageToWrite);
                } else {
                    mNfcReadFragment = (NFCReadFragment)getFragmentManager().findFragmentByTag(NFCReadFragment.TAG);
                    mNfcReadFragment.onNfcDetected(ndef);
                }
            }
        }
    }
}

دو تا Void به نام های showReadFragment و showWriteFragment به برای نمایش فرگمنت ها مورد استفاده قرار میگیرد. در بخش onResume شاید بشه گفت یه جورایی یک Listent تعریف شده تا در صورتی که کارتی که حاوی تگ بود به گوشی نزدیک شد یکسری عملیات انجام شود interfcae ها که تعریف شده بود نیز مشخص است onDialogDisplayed و onDialogDismissed که متوجه می شوید چه عملی در حال شبیه سازی کردن است. بخش onNewIntent به منظور گرفتن داده از فرگمنت است و بررسی می شود که آیا باید تگ نوشته شود یا Detect شود.
 
 
این آموزش هم به پایان رسید.
 
موفق و پیروز باشید.

مطالعه بیشتر