diff --git a/autonfc/build.properties b/autonfc/build.properties index b843769..c3fb692 100644 --- a/autonfc/build.properties +++ b/autonfc/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Mon Mar 16 02:31:00 GMT 2026 +#Mon Mar 16 03:39:59 GMT 2026 stageCount=0 libraryProject= baseVersion=15.11 publishVersion=15.0.0 -buildCount=12 +buildCount=27 baseBetaVersion=15.0.1 diff --git a/autonfc/src/main/AndroidManifest.xml b/autonfc/src/main/AndroidManifest.xml index 0c93ecd..9d8ea1c 100644 --- a/autonfc/src/main/AndroidManifest.xml +++ b/autonfc/src/main/AndroidManifest.xml @@ -8,8 +8,12 @@ - - + + + + + + - + - - - - - + + + + + + + + + + + + diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/MainActivity.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/MainActivity.java index 077489c..ac0a295 100644 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/MainActivity.java +++ b/autonfc/src/main/java/cc/winboll/studio/autonfc/MainActivity.java @@ -1,6 +1,9 @@ package cc.winboll.studio.autonfc; +import android.app.PendingIntent; import android.content.Intent; +import android.nfc.NfcAdapter; +import android.nfc.Tag; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; @@ -10,10 +13,15 @@ import androidx.appcompat.widget.Toolbar; import cc.winboll.studio.autonfc.nfc.AutoNFCService; import cc.winboll.studio.autonfc.nfc.NFCInterfaceActivity; import cc.winboll.studio.libappbase.LogActivity; +import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.ToastUtils; public class MainActivity extends AppCompatActivity { + private static final String TAG = "MainActivity"; + + private NfcAdapter mNfcAdapter; + private PendingIntent mPendingIntent; @Override protected void onCreate(Bundle savedInstanceState) { @@ -23,35 +31,69 @@ public class MainActivity extends AppCompatActivity { Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); - + // 初始化 NFC + mNfcAdapter = NfcAdapter.getDefaultAdapter(this); + Intent nfcIntent = new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + mPendingIntent = PendingIntent.getActivity(this, 0, nfcIntent, 0); + + LogUtils.d(TAG, "onCreate() -> NFC 监听已绑定到 MainActivity"); } @Override protected void onResume() { super.onResume(); + LogUtils.d(TAG, "onResume() -> 开启 NFC 前台分发"); + if (mNfcAdapter != null) { + mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null); + } + } + + @Override + protected void onPause() { + super.onPause(); + LogUtils.d(TAG, "onPause() -> 关闭 NFC 前台分发"); + if (mNfcAdapter != null) { + mNfcAdapter.disableForegroundDispatch(this); + } + } + + // NFC 卡片靠近唯一入口 + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + LogUtils.d(TAG, "onNewIntent() -> 检测到 NFC 卡片"); + + Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + if (tag == null) { + LogUtils.e(TAG, "onNewIntent() -> Tag 为空"); + return; + } + + // 转发给服务处理 + Intent serviceIntent = new Intent(this, AutoNFCService.class); + serviceIntent.putExtras(intent); + startService(serviceIntent); } - // 加载菜单 @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main_menu, menu); return true; } - // 菜单点击 @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.menu_start_service) { startService(new Intent(this, AutoNFCService.class)); - ToastUtils.show("NFC服务已启动"); + ToastUtils.show("NFC 服务已启动"); return true; } if (id == R.id.menu_stop_service) { stopService(new Intent(this, AutoNFCService.class)); - ToastUtils.show("NFC服务已停止"); + ToastUtils.show("NFC 服务已停止"); return true; } diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/AutoNFCService.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/AutoNFCService.java index b84f43c..37d6723 100644 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/AutoNFCService.java +++ b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/AutoNFCService.java @@ -7,85 +7,57 @@ import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; -import android.os.Build; -import android.os.IBinder; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.Ndef; -import android.widget.Toast; +import android.os.Build; +import android.os.IBinder; +import cc.winboll.studio.autonfc.MainActivity; import cc.winboll.studio.autonfc.R; import cc.winboll.studio.libappbase.LogUtils; import java.nio.charset.Charset; /* * 源码说明与描述: - * NFC 后台监听服务(正规保活版) - * 保活方式:前台服务 + START_STICKY + 任务销毁重启 + 开机自启 + * NFC 后台服务,仅处理 NFC 数据解析与业务逻辑,不负责监听 * * 作者:豆包&ZhanGSKen - * 创建时间:2026-03-16 16:10:00 - * 最后编辑时间:2026-03-16 18:20:00 + * 创建时间:2026-03-17 00:00:00 + * 最后编辑时间:2026-03-17 19:40:00 */ - public class AutoNFCService extends Service { public static final String TAG = "AutoNFCService"; private static final int NOTIFICATION_ID = 0x1001; - private static final String CHANNEL_ID = "AUTO_NFC_SERVICE_CHANNEL"; + private static final String CHANNEL_ID = "NFC_SERVICE_CHANNEL"; @Override public void onCreate() { super.onCreate(); - LogUtils.d(TAG, "onCreate"); + LogUtils.d(TAG, "onCreate() -> 服务创建"); startForeground(NOTIFICATION_ID, buildNotification()); } - private Notification buildNotification() { - NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - NotificationChannel channel = new NotificationChannel( - CHANNEL_ID, - "AutoNFC 后台监听", - NotificationManager.IMPORTANCE_LOW - ); - channel.setSound(null, null); - channel.setVibrationPattern(null); - nm.createNotificationChannel(channel); - } - - Intent intent = new Intent(this, NFCInterfaceActivity.class); - PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - - return new Notification.Builder(this) - .setChannelId(CHANNEL_ID) - .setSmallIcon(R.mipmap.ic_launcher) - .setContentTitle("AutoNFC 运行中") - .setContentText("NFC 卡片监听已启动") - .setContentIntent(pi) - .setSound(null) - .setVibrate(null) - .build(); - } - @Override public int onStartCommand(Intent intent, int flags, int startId) { - LogUtils.d(TAG, "onStartCommand"); + LogUtils.d(TAG, "onStartCommand() -> 服务运行"); + if (intent != null) { handleNfcIntent(intent); } return START_STICKY; } - public void handleNfcIntent(Intent intent) { + // 处理 MainActivity 转发的 NFC 数据 + private void handleNfcIntent(Intent intent) { String action = intent.getAction(); if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action) - || NfcAdapter.ACTION_TECH_DISCOVERED.equals(action) - || NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) { + || NfcAdapter.ACTION_TECH_DISCOVERED.equals(action) + || NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) { - LogUtils.d(TAG, "NFC 卡片已靠近"); + LogUtils.d(TAG, "handleNfcIntent() -> 收到 NFC 卡片"); Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); parseNdefData(tag); } @@ -93,13 +65,13 @@ public class AutoNFCService extends Service { private void parseNdefData(Tag tag) { if (tag == null) { - showToast("标签为空"); + LogUtils.e(TAG, "parseNdefData() -> tag is null"); return; } Ndef ndef = Ndef.get(tag); if (ndef == null) { - showToast("不支持NDEF格式"); + LogUtils.e(TAG, "parseNdefData() -> 不支持 NDEF"); return; } @@ -107,51 +79,58 @@ public class AutoNFCService extends Service { ndef.connect(); NdefMessage msg = ndef.getNdefMessage(); if (msg == null) { + LogUtils.w(TAG, "parseNdefData() -> 卡片无数据"); ndef.close(); - showToast("未读取到NDEF数据"); return; } NdefRecord[] records = msg.getRecords(); if (records != null && records.length > 0) { byte[] payload = records[0].getPayload(); - if (payload.length > 0) { - int langLen = payload[0] & 0x3F; - int start = 1 + langLen; - if (start < payload.length) { - String data = new String(payload, start, payload.length - start, Charset.forName("UTF-8")); - LogUtils.d(TAG, "读取数据:" + data); - // 只弹出Toast显示内容 - showToast("NFC数据:" + data); - } + int langLen = payload[0] & 0x3F; + int start = 1 + langLen; + + if (start < payload.length) { + String data = new String(payload, start, payload.length - start, Charset.forName("UTF-8")); + LogUtils.d(TAG, "parseNdefData() -> 读卡成功:" + data); } } ndef.close(); } catch (Exception e) { - LogUtils.e(TAG, "解析失败", e); - showToast("读取失败:" + e.getMessage()); + LogUtils.e(TAG, "parseNdefData() -> 读取失败", e); } } - /** - * 弹出Toast - */ - private void showToast(final String msg) { - Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show(); - } + private Notification buildNotification() { + NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - @Override - public void onTaskRemoved(Intent rootIntent) { - super.onTaskRemoved(rootIntent); - LogUtils.d(TAG, "任务被移除,重启服务"); - Intent intent = new Intent(getApplicationContext(), AutoNFCService.class); - startService(intent); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + "NFC 后台服务", + NotificationManager.IMPORTANCE_LOW + ); + channel.setSound(null, null); + channel.setVibrationPattern(null); + nm.createNotificationChannel(channel); + } + + Intent intent = new Intent(this, MainActivity.class); + PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + + return new Notification.Builder(this) + .setChannelId(CHANNEL_ID) + .setSmallIcon(R.mipmap.ic_launcher) + .setContentTitle("NFC 服务运行中") + .setContentText("正在监听卡片") + .setContentIntent(pi) + .build(); } @Override public void onDestroy() { super.onDestroy(); - LogUtils.d(TAG, "onDestroy"); + LogUtils.d(TAG, "onDestroy() -> 服务已停止"); } @Override diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NFCInterfaceActivity.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NFCInterfaceActivity.java index 17a5959..868860a 100644 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NFCInterfaceActivity.java +++ b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NFCInterfaceActivity.java @@ -1,233 +1,250 @@ -/* - - 源码说明与描述: - - NFC 调试界面,负责前台分发、UI 交互、写入控制 - - 作者:豆包&ZhanGSKenzhangsken@qq.com - - 创建时间:2026-03-16 10:00:00 - - 最后编辑时间:2026-03-16 18:59:00 - */ package cc.winboll.studio.autonfc.nfc; import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; -import android.content.IntentFilter; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; -import android.nfc.NfcManager; import android.nfc.Tag; import android.nfc.tech.Ndef; -import android.nfc.tech.NdefFormatable; import android.os.Bundle; -import android.view.View; -import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import android.view.View; import cc.winboll.studio.autonfc.R; import cc.winboll.studio.libappbase.LogUtils; import java.nio.charset.Charset; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +/* + * 源码说明与描述: + * NFC 操作界面(自主读卡 + 显示内容与时间 + 写入数据) + * 不再接收 AutoNFCService 消息 + * + * 作者:豆包&ZhanGSKen + * 创建时间:2026-03-16 16:10:00 + * 最后编辑时间:2026-03-17 10:45:00 + */ public class NFCInterfaceActivity extends Activity { -// ========================= 常量定义 ========================= - private static final String TAG = "NFCInterfaceActivity"; + private static final String TAG = "NFCInterfaceActivity"; -// ========================= 成员属性 ========================= - private NfcAdapter mNfcAdapter; - private PendingIntent mPendingIntent; - private IntentFilter[] mIntentFilters; - private String[][] mTechLists; + private EditText etInput; + private TextView tvResult; + private TextView tvStatus; - private TextView tvStatus; - private TextView tvData; - private EditText etWriteContent; - private Button btnReadNfc; - private Button btnWriteNfc; + private NfcAdapter mNfcAdapter; + private PendingIntent mNfcPendingIntent; + private Tag mCurrentTag; - private Tag mCurrentTag; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LogUtils.d(TAG, "onCreate()"); + setContentView(R.layout.activity_nfc_interface); -// ========================= 生命周期 ========================= - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - LogUtils.d(TAG, "onCreate() 调用,savedInstanceState: " + (savedInstanceState != null ? "非空" : "空")); - setContentView(R.layout.activity_nfc_interface); + initView(); + initNfc(); + } - tvStatus = (TextView) findViewById(R.id.tv_nfc_status); - tvData = (TextView) findViewById(R.id.tv_nfc_data); - etWriteContent = (EditText) findViewById(R.id.et_write_content); - btnReadNfc = (Button) findViewById(R.id.btn_read_nfc); - btnWriteNfc = (Button) findViewById(R.id.btn_write_nfc); + private void initView() { + LogUtils.d(TAG, "initView()"); + etInput = findViewById(R.id.et_input); + tvResult = findViewById(R.id.tv_result); + tvStatus = findViewById(R.id.tv_status); + } - initNFC(); - startAutoNFCService(); + private void initNfc() { + LogUtils.d(TAG, "initNfc()"); + mNfcAdapter = NfcAdapter.getDefaultAdapter(this); - btnReadNfc.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - LogUtils.d(TAG, "btnReadNfc 点击,进入等待读卡模式"); - updateStatus("等待卡片靠近..."); - } - }); + if (mNfcAdapter == null) { + tvStatus.setText("设备不支持NFC"); + return; + } + if (!mNfcAdapter.isEnabled()) { + tvStatus.setText("请在设置中开启NFC"); + return; + } - btnWriteNfc.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - String content = etWriteContent.getText().toString().trim(); - LogUtils.d(TAG, "btnWriteNfc 点击,待写入内容: " + content); - writeNfc(content); - } - }); - } + mNfcPendingIntent = PendingIntent.getActivity( + this, 0, + new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), + PendingIntent.FLAG_UPDATE_CURRENT + ); -// ========================= 初始化 ========================= - private void initNFC() { - LogUtils.d(TAG, "initNFC() 开始初始化 NFC 配置"); - NfcManager manager = (NfcManager) getSystemService(NFC_SERVICE); - mNfcAdapter = manager.getDefaultAdapter(); + tvStatus.setText("NFC已启动,等待卡片靠近"); + } - if (mNfcAdapter == null) { - LogUtils.e(TAG, "initNFC() 设备不支持 NFC"); - updateStatus("设备不支持NFC"); - return; - } + @Override + protected void onResume() { + super.onResume(); + LogUtils.d(TAG, "onResume() -> 启用前台读卡"); + enableForegroundDispatch(); + } - Intent intent = new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - mPendingIntent = PendingIntent.getActivity(this, 0, intent, 0); + @Override + protected void onPause() { + super.onPause(); + LogUtils.d(TAG, "onPause() -> 停止前台读卡"); + disableForegroundDispatch(); + mCurrentTag = null; + } - IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); - try { - filter.addDataType("/"); - } catch (IntentFilter.MalformedMimeTypeException e) { - LogUtils.e(TAG, "initNFC() 过滤器异常: " + e.getMessage(), e); - } + private void enableForegroundDispatch() { + if (mNfcAdapter != null && mNfcAdapter.isEnabled()) { + mNfcAdapter.enableForegroundDispatch(this, mNfcPendingIntent, null, null); + } + } - mIntentFilters = new IntentFilter[] { filter }; - mTechLists = new String[][] { - { Ndef.class.getName() }, - { NdefFormatable.class.getName() } - }; - LogUtils.d(TAG, "initNFC() 完成 NFC 前台分发配置"); - updateStatus("NFC 初始化完成"); - } + private void disableForegroundDispatch() { + if (mNfcAdapter != null) { + mNfcAdapter.disableForegroundDispatch(this); + } + } - private void startAutoNFCService() { - LogUtils.d(TAG, "startAutoNFCService() 启动后台监听服务"); - Intent service = new Intent(this, AutoNFCService.class); - startService(service); - } + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + LogUtils.d(TAG, "onNewIntent() -> 卡片已靠近"); -// ========================= 生命周期回调 ========================= - @Override - protected void onResume() { - super.onResume(); - LogUtils.d(TAG, "onResume() 启用前台分发"); - if (mNfcAdapter != null) { - mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, mIntentFilters, mTechLists); - } - } + mCurrentTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + tvStatus.setText("卡片已连接"); - @Override - protected void onPause() { - super.onPause(); - LogUtils.d(TAG, "onPause() 关闭前台分发"); - if (mNfcAdapter != null) { - mNfcAdapter.disableForegroundDispatch(this); - } - } + // 自动读取旧数据 + readOldNfcData(mCurrentTag); + } - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - LogUtils.d(TAG, "onNewIntent() 收到 NFC 卡片意图: " + intent.getAction()); - mCurrentTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + /** + * 读取卡片原有数据并显示(带时间) + */ + private void readOldNfcData(Tag tag) { + LogUtils.d(TAG, "readOldNfcData(tag=" + tag + ")"); - Intent serviceIntent = new Intent(this, AutoNFCService.class); - serviceIntent.putExtras(intent); - startService(serviceIntent); + if (tag == null) { + showToast("标签为空"); + return; + } - updateStatus("卡片已靠近,数据解析中..."); - } + Ndef ndef = Ndef.get(tag); + if (ndef == null) { + showToast("不支持NDEF"); + return; + } -// ========================= 写入逻辑 ========================= - public boolean writeNfc(String text) { - LogUtils.d(TAG, "writeNfc() 写入文本: " + text); - if (mCurrentTag == null) { - LogUtils.w(TAG, "writeNfc() 未检测到卡片,无法写入"); - updateStatus("请先靠近卡片"); - return false; - } - if (text.isEmpty()) { - LogUtils.w(TAG, "writeNfc() 写入内容为空"); - updateStatus("输入内容不能为空"); - return false; - } + try { + ndef.connect(); + NdefMessage msg = ndef.getNdefMessage(); + if (msg == null) { + tvResult.setText("无旧数据"); + ndef.close(); + return; + } - NdefRecord record = createTextRecord(text); - NdefMessage msg = new NdefMessage(new NdefRecord[] { record }); + NdefRecord[] records = msg.getRecords(); + if (records != null && records.length > 0) { + byte[] payload = records[0].getPayload(); + int langLen = payload[0] & 0x3F; + int start = 1 + langLen; - try { - Ndef ndef = Ndef.get(mCurrentTag); - if (ndef != null) { - ndef.connect(); - if (ndef.isWritable()) { - ndef.writeNdefMessage(msg); - ndef.close(); - LogUtils.i(TAG, "writeNfc() 写入成功"); - updateStatus("写入成功"); - updateData(text); - NfcStateMonitor.notifyWriteSuccess(); - return true; - } - } else { - NdefFormatable f = NdefFormatable.get(mCurrentTag); - if (f != null) { - f.connect(); - f.format(msg); - f.close(); - LogUtils.i(TAG, "writeNfc() 格式化并写入成功"); - updateStatus("格式化并写入成功"); - NfcStateMonitor.notifyWriteSuccess(); - return true; - } - } - } catch (Exception e) { - LogUtils.e(TAG, "writeNfc() 写入异常: " + e.getMessage(), e); - updateStatus("写入失败:" + e.getMessage()); - NfcStateMonitor.notifyWriteFail(e.getMessage()); - } - return false; - } + String content = new String( + payload, start, payload.length - start, + Charset.forName("UTF-8") + ); -// ========================= NDEF 构造 ========================= - private NdefRecord createTextRecord(String text) { - LogUtils.d(TAG, "createTextRecord() 构造 NDEF 文本记录"); - byte[] langBytes = "en".getBytes(Charset.forName("UTF-8")); - byte[] textBytes = text.getBytes(Charset.forName("UTF-8")); - byte[] payload = new byte[1 + langBytes.length + textBytes.length]; - payload[0] = (byte) langBytes.length; - System.arraycopy(langBytes, 0, payload, 1, langBytes.length); - System.arraycopy(textBytes, 0, payload, 1 + langBytes.length, textBytes.length); - return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payload); - } + // 附加时间 + String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(new Date()); + String show = "读取时间:" + time + "\n\n内容:\n" + content; + tvResult.setText(show); -// ========================= UI 更新 ========================= - private void updateStatus(final String s) { - runOnUiThread(new Runnable() { - @Override - public void run() { - tvStatus.setText("状态:" + s); - } - }); - } + showToast("读取成功"); + } + ndef.close(); + } catch (Exception e) { + LogUtils.e(TAG, "读取失败", e); + tvResult.setText("读取失败:" + e.getMessage()); + showToast("读取失败"); + } + } + + /** + * 写入按钮点击 + */ + public void onWriteClick(View view) { + LogUtils.d(TAG, "onWriteClick()"); + String text = etInput.getText().toString().trim(); + + if (text.isEmpty()) { + showToast("请输入内容"); + return; + } + if (mCurrentTag == null) { + showToast("请先靠近卡片"); + return; + } + + writeNfc(mCurrentTag, text); + } + + /** + * 写入NDEF + */ + private void writeNfc(Tag tag, String text) { + LogUtils.d(TAG, "writeNfc(text=" + text + ")"); + tvStatus.setText("正在写入..."); + + try { + Ndef ndef = Ndef.get(tag); + if (ndef == null) { + showToast("不支持NDEF"); + tvStatus.setText("不支持NDEF"); + return; + } + + ndef.connect(); + NdefRecord record = createTextRecord(text, true); + NdefMessage msg = new NdefMessage(new NdefRecord[]{record}); + ndef.writeNdefMessage(msg); + ndef.close(); + + tvStatus.setText("写入成功!"); + showToast("写入成功"); + + // 写入后重新读取 + readOldNfcData(tag); + } catch (Exception e) { + LogUtils.e(TAG, "写入失败", e); + tvStatus.setText("写入失败"); + showToast("写入失败"); + } + } + + /** + * 创建文本记录 + */ + private NdefRecord createTextRecord(String text, boolean isUtf8) { + byte[] langBytes = "en".getBytes(Charset.forName("US-ASCII")); + byte[] textBytes = text.getBytes(Charset.forName(isUtf8 ? "UTF-8" : "UTF-16")); + int status = isUtf8 ? 0 : 0x80; + status |= langBytes.length & 0x3F; + + byte[] data = new byte[1 + langBytes.length + textBytes.length]; + data[0] = (byte) status; + System.arraycopy(langBytes, 0, data, 1, langBytes.length); + System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length); + + return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, + NdefRecord.RTD_TEXT, new byte[0], data); + } + + private void showToast(String msg) { + Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); + } - private void updateData(final String s) { - runOnUiThread(new Runnable() { - @Override - public void run() { - tvData.setText("内容:\n" + s); - } - }); - } } + diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcProxyActivity.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcProxyActivity.java new file mode 100644 index 0000000..cd0b218 --- /dev/null +++ b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcProxyActivity.java @@ -0,0 +1,97 @@ +package cc.winboll.studio.autonfc.nfc; + +/* + * 源码说明与描述: + * NFC 透明代理 Activity,用于在后台捕获 NFC 卡片事件并转发给 AutoNFCService + * 界面为 1 像素透明,不影响用户操作 + * + * 作者:豆包&ZhanGSKen + * 创建时间:2026-03-17 14:00:00 + * 最后编辑时间:2026-03-17 18:30:00 + */ +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Intent; +import android.nfc.NfcAdapter; +import android.os.Bundle; +import android.view.Window; +import android.view.WindowManager; +import cc.winboll.studio.libappbase.LogUtils; + +public class NfcProxyActivity extends Activity { + + private static final String TAG = "NfcProxyActivity"; + + private NfcAdapter mNfcAdapter; + private PendingIntent mPendingIntent; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LogUtils.d(TAG, "onCreate() -> 启动 NFC 透明代理"); + + // 设置为 1 像素透明窗口,用户不可见 + Window window = getWindow(); + window.setLayout(1, 1); + window.setBackgroundDrawableResource(android.R.color.transparent); + window.setFlags( + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + ); + + // 初始化 NFC + mNfcAdapter = NfcAdapter.getDefaultAdapter(this); + Intent intent = new Intent(this, getClass()) + .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + mPendingIntent = PendingIntent.getActivity(this, 0, intent, 0); + } + + @Override + protected void onResume() { + super.onResume(); + LogUtils.d(TAG, "onResume() -> 启用前台分发"); + + // 强制获取焦点,确保 enableForegroundDispatch 生效 + getWindow().getDecorView().requestFocus(); + getWindow().setFlags( + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + 0 // 临时允许获取焦点 + ); + + if (mNfcAdapter != null) { + mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null); + } + + // 恢复为不可聚焦,避免拦截用户操作 + getWindow().setFlags( + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + ); + } + + @Override + protected void onPause() { + super.onPause(); + LogUtils.d(TAG, "onPause() -> 关闭前台分发"); + if (mNfcAdapter != null) { + mNfcAdapter.disableForegroundDispatch(this); + } + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + LogUtils.d(TAG, "onNewIntent() -> 捕获到 NFC 卡片事件"); + + // 转发给 AutoNFCService 处理 + Intent serviceIntent = new Intent(this, AutoNFCService.class); + serviceIntent.putExtras(intent); + startService(serviceIntent); + } +} + diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcStateMonitor.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcStateMonitor.java index addd2bc..a58fead 100644 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcStateMonitor.java +++ b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcStateMonitor.java @@ -1,16 +1,10 @@ package cc.winboll.studio.autonfc.nfc; -import android.nfc.NfcAdapter; import java.util.HashMap; import java.util.Map; -/** - * @Author 豆包&ZhanGSKen - * @Date 2026/03/14 13:34 - */ public class NfcStateMonitor { - - private static Map sListenerMap; + private static Map sListenerMap = new HashMap<>(); private static boolean sIsRunning = false; public static void startMonitor() { @@ -28,6 +22,7 @@ public class NfcStateMonitor { } } + // 你原来的方法名:registerListener public static void registerListener(String key, OnNfcStateListener listener) { if (!sIsRunning || listener == null) return; sListenerMap.put(key, listener); @@ -40,32 +35,44 @@ public class NfcStateMonitor { public static void notifyNfcConnected() { if (!sIsRunning) return; - for (OnNfcStateListener l : sListenerMap.values()) l.onNfcConnected(); + for (OnNfcStateListener l : sListenerMap.values()) { + l.onNfcConnected(); + } } public static void notifyNfcDisconnected() { if (!sIsRunning) return; - for (OnNfcStateListener l : sListenerMap.values()) l.onNfcDisconnected(); + for (OnNfcStateListener l : sListenerMap.values()) { + l.onNfcDisconnected(); + } } public static void notifyReadSuccess(String data) { if (!sIsRunning) return; - for (OnNfcStateListener l : sListenerMap.values()) l.onNfcReadSuccess(data); + for (OnNfcStateListener l : sListenerMap.values()) { + l.onNfcReadSuccess(data); + } } public static void notifyReadFail(String error) { if (!sIsRunning) return; - for (OnNfcStateListener l : sListenerMap.values()) l.onNfcReadFail(error); + for (OnNfcStateListener l : sListenerMap.values()) { + l.onNfcReadFail(error); + } } public static void notifyWriteSuccess() { if (!sIsRunning) return; - for (OnNfcStateListener l : sListenerMap.values()) l.onNfcWriteSuccess(); + for (OnNfcStateListener l : sListenerMap.values()) { + l.onNfcWriteSuccess(); + } } public static void notifyWriteFail(String error) { if (!sIsRunning) return; - for (OnNfcStateListener l : sListenerMap.values()) l.onNfcWriteFail(error); + for (OnNfcStateListener l : sListenerMap.values()) { + l.onNfcWriteFail(error); + } } } diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/OnNfcStateListener.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/OnNfcStateListener.java index 9ce8c2b..4b149d5 100644 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/OnNfcStateListener.java +++ b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/OnNfcStateListener.java @@ -1,11 +1,7 @@ package cc.winboll.studio.autonfc.nfc; -/** - * @Author 豆包&ZhanGSKen - * @Date 2026/03/14 13:33 - */ public interface OnNfcStateListener { - void onNfcConnected(); + void onNfcConnected(); // 无参数! void onNfcDisconnected(); void onNfcReadSuccess(String data); void onNfcReadFail(String error); diff --git a/autonfc/src/main/res/layout/activity_nfc_interface.xml b/autonfc/src/main/res/layout/activity_nfc_interface.xml index a4e0110..fa95874 100644 --- a/autonfc/src/main/res/layout/activity_nfc_interface.xml +++ b/autonfc/src/main/res/layout/activity_nfc_interface.xml @@ -1,50 +1,46 @@ - + android:padding="20dp"> + + android:textColor="#FF666666"/> - + android:hint="输入要写入的NFC文本" + android:minHeight="50dp" + android:layout_marginTop="10dp"/>