diff --git a/app/build.gradle b/app/build.gradle
index 9fadb35..b1b4d79 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -9,14 +9,14 @@ def releaseTime() {
}
android {
- compileSdkVersion 33
- buildToolsVersion "30.0.3"
+ compileSdkVersion 29
+// buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.ttstd.elderlyassistant"
//There are no CERT files because If the mini sdk version is 23+, the AGP will ignore the V1 scheme signature.
minSdkVersion 23
- targetSdkVersion 33
+ targetSdkVersion 29
versionCode 1
versionName "1.0"
@@ -92,15 +92,49 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.3.1'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.3'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test.ext:junit:1.2.1'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+
+ //glide
+ implementation 'com.github.bumptech.glide:glide:4.13.1'
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.13.1'
+ //RxJava
+ implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
+ implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
+ //
+ implementation 'com.squareup.okhttp3:okhttp:4.7.0'
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
+// implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
+ implementation "com.squareup.retrofit2:adapter-rxjava3:2.9.0"
+ //Gson
+ implementation 'com.google.code.gson:gson:2.9.0'
+ implementation 'com.google.zxing:core:3.5.0'
+ //生命周期管理
+ implementation 'com.trello.rxlifecycle4:rxlifecycle:4.0.2'
+ implementation 'com.trello.rxlifecycle4:rxlifecycle-android:4.0.2'
+ implementation 'com.trello.rxlifecycle4:rxlifecycle-components:4.0.2'
+ implementation 'com.trello.rxlifecycle4:rxlifecycle-components-preference:4.0.2'
+ implementation 'com.trello.rxlifecycle4:rxlifecycle-android-lifecycle:4.0.2'
+
+ implementation 'com.jakewharton.rxbinding4:rxbinding:4.0.0'
+ /*https://github.com/JeremyLiao/LiveEventBus*/
+ implementation 'com.jeremyliao:live-event-bus-x:1.7.3'
//MMKV
implementation 'com.tencent:mmkv-static:1.2.14'
//bugly
implementation 'com.tencent.bugly:crashreport:4.1.9.2'
implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.0.0'
+
+ //工具类
+ implementation 'com.blankj:utilcodex:1.31.0'
+ //autosize会改变第三方view的大小
+ //https://github.com/JessYanCoding/AndroidAutoSize
+ implementation 'me.jessyan:autosize:1.2.1'
+ // 吐司框架:https://github.com/getActivity/Toaster
+ implementation 'com.github.getActivity:Toaster:12.6'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9e6a782..da6e42c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,11 +5,13 @@
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -58,7 +71,12 @@
android:resource="@xml/accessibility_service_config" />
-
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/ttstd/elderlyassistant/activity/main/MainActivity.java b/app/src/main/java/com/ttstd/elderlyassistant/activity/main/MainActivity.java
index b5425af..d0f7aeb 100644
--- a/app/src/main/java/com/ttstd/elderlyassistant/activity/main/MainActivity.java
+++ b/app/src/main/java/com/ttstd/elderlyassistant/activity/main/MainActivity.java
@@ -1,16 +1,127 @@
package com.ttstd.elderlyassistant.activity.main;
-import androidx.appcompat.app.AppCompatActivity;
-
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.NdefMessage;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcManager;
+import android.nfc.Tag;
+import android.nfc.tech.MifareClassic;
+import android.nfc.tech.Ndef;
+import android.nfc.tech.NfcF;
import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.databinding.DataBindingUtil;
import com.ttstd.elderlyassistant.R;
+import com.ttstd.elderlyassistant.activity.nfc.NfcActivity;
+import com.ttstd.elderlyassistant.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
+ private static final String TAG = "MainActivity";
+
+ private ActivityMainBinding mBinding;
+
+ private NfcManager mNfcManager;
+ private NfcAdapter mNfcAdapter;
+ private Tag mTag;
+ private PendingIntent pendingIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
+// setContentView(R.layout.activity_main);
+ mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
+ mBinding.setClick(new BtnClick());
+
+
+ mNfcManager = (NfcManager) getSystemService(Context.NFC_SERVICE);
+ if (mNfcManager == null) {
+ // 设备不支持 NFC
+ Toast.makeText(this, "设备不支持 NFC", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ mNfcAdapter = mNfcManager.getDefaultAdapter();
+ if (mNfcAdapter == null) {
+ // 设备不支持 NFC
+ Toast.makeText(this, "设备不支持 NFC", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ // 创建 PendingIntent 捕获 NFC 事件
+ pendingIntent = PendingIntent.getActivity(
+ this, 0,
+ new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
+ PendingIntent.FLAG_IMMUTABLE
+ );
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mNfcAdapter != null) {
+ // 指定处理 TECH_DISCOVERED 类型标签
+ IntentFilter[] filters = new IntentFilter[]{
+ new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)
+ };
+ String[][] techLists = new String[][]{
+ {NfcF.class.getName(), MifareClassic.class.getName()}
+ };
+ mNfcAdapter.enableForegroundDispatch(this, pendingIntent, filters, techLists);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mNfcAdapter != null) {
+ mNfcAdapter.disableForegroundDispatch(this);
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ Log.e(TAG, "onNewIntent: ");
+ if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
+ Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
+ if (tag != null) {
+ // 获取 NFC ID (16 进制格式)
+ byte[] idBytes = tag.getId();
+ String tagId = bytesToHex(idBytes);
+
+ // 读取 NDEF 数据(如为文本标签)
+ Ndef ndef = Ndef.get(tag);
+ if (ndef != null) {
+ NdefMessage ndefMessage = ndef.getCachedNdefMessage();
+ if (ndefMessage != null) {
+ String payload = new String(ndefMessage.getRecords()[0].getPayload());
+ Log.e(TAG, "onNewIntent: payload = " + payload);
+ }
+ }
+ }
+ }
+ }
+
+ // 字节数组转 Hex 字符串
+ private String bytesToHex(byte[] bytes) {
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes) {
+ sb.append(String.format("%02X", b));
+ }
+ return sb.toString();
+ }
+
+ public class BtnClick {
+ public void wirteTag(View view) {
+ startActivity(new Intent(MainActivity.this, NfcActivity.class));
+ }
}
}
diff --git a/app/src/main/java/com/ttstd/elderlyassistant/activity/nfc/NfcActivity.java b/app/src/main/java/com/ttstd/elderlyassistant/activity/nfc/NfcActivity.java
index 3354edc..8b260dd 100644
--- a/app/src/main/java/com/ttstd/elderlyassistant/activity/nfc/NfcActivity.java
+++ b/app/src/main/java/com/ttstd/elderlyassistant/activity/nfc/NfcActivity.java
@@ -1,64 +1,183 @@
package com.ttstd.elderlyassistant.activity.nfc;
-import androidx.appcompat.app.AppCompatActivity;
-
import android.content.Context;
import android.content.Intent;
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.os.Bundle;
import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
import android.widget.Toast;
-import com.ttstd.elderlyassistant.R;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.databinding.DataBindingUtil;
+import com.hjq.toast.Toaster;
+import com.ttstd.elderlyassistant.R;
+import com.ttstd.elderlyassistant.bean.Contact;
+import com.ttstd.elderlyassistant.databinding.ActivityNfcBinding;
+
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Locale;
+
+import com.ttstd.elderlyassistant.gson.GsonUtils;
public class NfcActivity extends AppCompatActivity {
+ private static final String TAG = "NfcActivity";
+
+ private ActivityNfcBinding mBinding;
+
+ private NfcManager mNfcManager;
+ private NfcAdapter mNfcAdapter;
+ private Tag mTag;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_nfc);
+ mBinding = DataBindingUtil.setContentView(this, R.layout.activity_nfc);
+// setContentView(R.layout.activity_nfc);
+ mBinding.setClick(new BtnClick());
- NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
- NfcAdapter nfcAdapter = manager.getDefaultAdapter();
-
- if (nfcAdapter == null) {
+ mNfcManager = (NfcManager) getSystemService(Context.NFC_SERVICE);
+ if (mNfcManager == null) {
// 设备不支持 NFC
Toast.makeText(this, "设备不支持 NFC", Toast.LENGTH_SHORT).show();
finish();
}
+ mNfcAdapter = mNfcManager.getDefaultAdapter();
+ if (mNfcAdapter == null) {
+ // 设备不支持 NFC
+ Toast.makeText(this, "设备不支持 NFC", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ Intent intent = getIntent();
+ getMessageFromIntent(intent);
+
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
- if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
- Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
- if (rawMessages != null) {
- NdefMessage[] messages = new NdefMessage[rawMessages.length];
- for (int i = 0; i < rawMessages.length; i++) {
- messages[i] = (NdefMessage) rawMessages[i];
+ getMessageFromIntent(intent);
+ }
+
+ private void getMessageFromIntent(Intent intent) {
+ String action = intent.getAction();
+ Log.e(TAG, "getMessageFromIntent: action = " + action);
+ switch (action) {
+ case NfcAdapter.ACTION_NDEF_DISCOVERED:
+ Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
+ if (rawMessages != null) {
+ NdefMessage[] messages = new NdefMessage[rawMessages.length];
+ for (int i = 0; i < rawMessages.length; i++) {
+ messages[i] = (NdefMessage) rawMessages[i];
+ }
+ // 处理 NDEF 消息
+ processNdefMessages(messages);
}
- // 处理 NDEF 消息
- processNdefMessages(messages);
- }
+ break;
+ case NfcAdapter.ACTION_TECH_DISCOVERED:
+ mTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
+ if (mTag != null) {
+ // 获取 NFC ID (16 进制格式)
+ byte[] idBytes = mTag.getId();
+ String tagId = bytesToHex(idBytes);
+ Log.e(TAG, "getMessageFromIntent: tagId = " + tagId);
+ // 读取 NDEF 数据(如为文本标签)
+ Ndef ndef = Ndef.get(mTag);
+ if (ndef != null) {
+ NdefMessage ndefMessage = ndef.getCachedNdefMessage();
+ if (ndefMessage != null) {
+ String payload = new String(ndefMessage.getRecords()[0].getPayload());
+ Log.e(TAG, "getMessageFromIntent: payload = " + payload);
+ } else {
+ Log.e(TAG, "getMessageFromIntent: ndefMessage is null");
+ }
+ }
+ }
+ break;
}
}
+ // 字节数组转 Hex 字符串
+ private String bytesToHex(byte[] bytes) {
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes) {
+ sb.append(String.format("%02X", b));
+ }
+ return sb.toString();
+ }
+
private void processNdefMessages(NdefMessage[] messages) {
for (NdefMessage message : messages) {
for (NdefRecord record : message.getRecords()) {
if (record.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) {
// 处理文本记录
String text = new String(record.getPayload());
- Toast.makeText(this, "NFC 数据: " + text, Toast.LENGTH_SHORT).show();
+ Toaster.show("NFC 数据: " + text);
+
}
}
}
}
+
+ public void writeTag(String text, Tag tag) {
+ try {
+ NdefRecord record = createTextRecord(text, Locale.ENGLISH);
+ NdefMessage message = new NdefMessage(new NdefRecord[]{record});
+
+ Ndef ndef = Ndef.get(tag);
+ if (ndef != null) {
+ ndef.connect();
+ ndef.writeNdefMessage(message);
+ Toaster.show("写入成功: ");
+ }
+ } catch (Exception e) {
+ Toaster.show("写入失败: " + e.getMessage());
+ }
+ }
+
+ // 创建 NDEF 文本记录
+ private NdefRecord createTextRecord(String text, Locale locale) {
+ byte[] langBytes = locale.getLanguage().getBytes(StandardCharsets.US_ASCII);
+ byte[] textBytes = text.getBytes(StandardCharsets.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
+ );
+ }
+
+ public class BtnClick {
+ public void write(View view) {
+ String name = mBinding.etName.getText().toString();
+ String phone = mBinding.etPhone.getText().toString();
+ if (TextUtils.isEmpty(name)) {
+ Toaster.show("请输入联系人姓名");
+ return;
+ }
+ if (TextUtils.isEmpty(phone)) {
+ Toaster.show("请输入联系人手机号");
+ return;
+ }
+ Contact contact = new Contact(name, phone);
+ writeTag(GsonUtils.toJSONString(contact), mTag);
+ }
+ }
}
diff --git a/app/src/main/java/com/ttstd/elderlyassistant/base/BaseApplication.java b/app/src/main/java/com/ttstd/elderlyassistant/base/BaseApplication.java
new file mode 100644
index 0000000..460d4ba
--- /dev/null
+++ b/app/src/main/java/com/ttstd/elderlyassistant/base/BaseApplication.java
@@ -0,0 +1,29 @@
+package com.ttstd.elderlyassistant.base;
+
+import android.app.Application;
+
+import com.hjq.toast.Toaster;
+
+public class BaseApplication extends Application {
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Toaster.init(this);
+ }
+
+ @Override
+ public void onTerminate() {
+ super.onTerminate();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ }
+}
diff --git a/app/src/main/java/com/ttstd/elderlyassistant/bean/Contact.java b/app/src/main/java/com/ttstd/elderlyassistant/bean/Contact.java
new file mode 100644
index 0000000..d3f443e
--- /dev/null
+++ b/app/src/main/java/com/ttstd/elderlyassistant/bean/Contact.java
@@ -0,0 +1,53 @@
+package com.ttstd.elderlyassistant.bean;
+
+import java.io.Serializable;
+
+public class Contact implements Serializable {
+ private static final long serialVersionUID = -5570426400839799887L;
+
+ String name;
+ String phone;
+ String tag;
+ /**
+ * 0 电话 1 短信 2 微信语音 3 微信视频
+ */
+ int defaultOperation;
+ int version;
+
+
+ public Contact() {
+
+ }
+
+ public Contact(String name, String phone) {
+ this.name = name;
+ this.phone = phone;
+ this.defaultOperation = 3;
+ this.version = 1;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getPhone() {
+ return phone;
+ }
+
+ public void setPhone(String phone) {
+ this.phone = phone;
+ }
+
+
+ public String getTag() {
+ return tag;
+ }
+
+ public void setTag(String tag) {
+ this.tag = tag;
+ }
+}
diff --git a/app/src/main/java/com/ttstd/elderlyassistant/gson/GsonUtils.java b/app/src/main/java/com/ttstd/elderlyassistant/gson/GsonUtils.java
new file mode 100644
index 0000000..c8b11ca
--- /dev/null
+++ b/app/src/main/java/com/ttstd/elderlyassistant/gson/GsonUtils.java
@@ -0,0 +1,144 @@
+package com.ttstd.elderlyassistant.gson;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.reflect.TypeToken;
+
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+
+public class GsonUtils {
+ //https://blog.csdn.net/zte1055889498/article/details/122400299
+
+ public static JsonObject getJsonObject(String jsonString) {
+ JsonObject jsonObject = JsonParser.parseString(jsonString).getAsJsonObject();
+ return jsonObject;
+ }
+
+ private static final Gson gson;
+
+ static {
+ GsonBuilder builder = new GsonBuilder();
+ builder.registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory());
+ builder.registerTypeAdapter(Integer.class, new IntegerDefault0Adapter());
+ builder.registerTypeAdapter(int.class, new IntegerDefault0Adapter());
+ builder.disableHtmlEscaping();
+ builder.enableComplexMapKeySerialization();
+ // builder.excludeFieldsWithoutExposeAnnotation();
+ builder.setDateFormat("yyyy-MM-dd HH:mm:ss");
+ gson = builder.create();
+ }
+
+ public static Type makeJavaType(Type rawType, Type... typeArguments) {
+ return TypeToken.getParameterized(rawType, typeArguments).getType();
+ }
+
+ public static String toString(Object value) {
+ if (Objects.isNull(value)) {
+ return null;
+ }
+ if (value instanceof String) {
+ return (String) value;
+ }
+ return toJSONString(value);
+ }
+
+ public static String toJSONString(Object value) {
+ return gson.toJson(value);
+ }
+
+ public static String toPrettyString(Object value) {
+ return gson.newBuilder().setPrettyPrinting().create().toJson(value);
+ }
+
+ public static JsonElement fromJavaObject(Object value) {
+ JsonElement result = null;
+ if (Objects.nonNull(value) && (value instanceof String)) {
+ result = parseObject((String) value);
+ } else {
+ result = gson.toJsonTree(value);
+ }
+ return result;
+ }
+
+ public static JsonElement parseObject(String content) {
+ return JsonParser.parseString(content);
+ }
+
+ public static JsonElement getJsonElement(JsonObject node, String name) {
+ return node.get(name);
+ }
+
+ public static JsonElement getJsonElement(JsonArray node, int index) {
+ return node.get(index);
+ }
+
+ public static T toJavaObject(JsonElement node, Class clazz) {
+ return gson.fromJson(node, clazz);
+ }
+
+ public static T toJavaObject(JsonElement node, Type type) {
+ return gson.fromJson(node, type);
+ }
+
+ public static T toJavaObject(JsonElement node, TypeToken> typeToken) {
+ return toJavaObject(node, typeToken.getType());
+ }
+
+ public static List toJavaList(JsonElement node, Class clazz) {
+ return toJavaObject(node, makeJavaType(List.class, clazz));
+ }
+
+ public static List