读取nfc数据和写入数据

This commit is contained in:
2025-06-26 08:44:18 +08:00
parent 11987cb90e
commit 1cd1f2debd
12 changed files with 713 additions and 59 deletions

View File

@@ -5,11 +5,13 @@
<!--NFC权限-->
<uses-permission android:name="android.permission.NFC" />
<!--声明只有带NFC的手机才可以下载-->
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
<application
android:name=".base.BaseApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
@@ -24,28 +26,39 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.nfc.NfcActivity"
android:exported="true">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" /> <!-- 指定数据类型 -->
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
</activity>
<activity
android:name=".activity.nfc.NfcActivity"
android:exported="true"
android:launchMode="singleTop">
<!-- <intent-filter>-->
<!-- <action android:name="android.nfc.action.NDEF_DISCOVERED" />-->
<!-- <category android:name="android.intent.category.DEFAULT" />-->
<!-- <data android:mimeType="text/plain" />-->
<!-- </intent-filter>-->
<!-- <intent-filter>-->
<!-- <action android:name="android.nfc.action.TECH_DISCOVERED" />-->
<!-- </intent-filter>-->
<!-- <meta-data-->
<!-- android:name="android.nfc.action.TECH_DISCOVERED"-->
<!-- android:resource="@xml/nfc_tech_filter" />-->
</activity>
<service
android:name="com.google.android.accessibility.selecttospeak.SelectToSpeakService"
android:label="@string/accessibility_service_label"
android:exported="true"
android:label="@string/accessibility_service_label"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<!-- android:priority="10000" 可提高服务在设置中的权重,排在前面 -->
@@ -58,7 +71,12 @@
android:resource="@xml/accessibility_service_config" />
</service>
<meta-data
android:name="design_width_in_dp"
android:value="360" />
<meta-data
android:name="design_height_in_dp"
android:value="640" />
</application>
</manifest>

View File

@@ -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));
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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> T toJavaObject(JsonElement node, Class<T> clazz) {
return gson.fromJson(node, clazz);
}
public static <T> T toJavaObject(JsonElement node, Type type) {
return gson.fromJson(node, type);
}
public static <T> T toJavaObject(JsonElement node, TypeToken<?> typeToken) {
return toJavaObject(node, typeToken.getType());
}
public static <E> List<E> toJavaList(JsonElement node, Class<E> clazz) {
return toJavaObject(node, makeJavaType(List.class, clazz));
}
public static List<Object> toJavaList(JsonElement node) {
return toJavaObject(node, new TypeToken<List<Object>>() {
}.getType());
}
public static <V> Map<String, V> toJavaMap(JsonElement node, Class<V> clazz) {
return toJavaObject(node, makeJavaType(Map.class, String.class, clazz));
}
public static Map<String, Object> toJavaMap(JsonElement node) {
return toJavaObject(node, new TypeToken<Map<String, Object>>() {
}.getType());
}
public static <T> T toJavaObject(String content, Class<T> clazz) {
JsonObject jsonObject = getJsonObject(content);
String jsonString = jsonObject.toString();
return gson.fromJson(jsonString, clazz);
}
public static <T> T toJavaObject(String content, Type type) {
return gson.fromJson(content, type);
}
public static <T> T toJavaObject(String content, TypeToken<?> typeToken) {
return toJavaObject(content, typeToken.getType());
}
public static <E> List<E> toJavaList(String content, Class<E> clazz) {
return toJavaObject(content, makeJavaType(List.class, clazz));
}
public static List<Object> toJavaList(String content) {
return toJavaObject(content, new TypeToken<List<Object>>() {
}.getType());
}
public static <V> Map<String, V> toJavaMap(String content, Class<V> clazz) {
return toJavaObject(content, makeJavaType(Map.class, String.class, clazz));
}
public static Map<String, Object> toJavaMap(String content) {
return toJavaObject(content, new TypeToken<Map<String, Object>>() {
}.getType());
}
}

View File

@@ -0,0 +1,35 @@
package com.ttstd.elderlyassistant.gson;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonSyntaxException;
import java.lang.reflect.Type;
public class IntegerDefault0Adapter implements JsonSerializer<Integer>, JsonDeserializer<Integer> {
@Override
public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
try {
if (json.getAsString().equals("")) {
return 0;
}
} catch (Exception ignore) {
}
try {
return json.getAsInt();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override
public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
}

View File

@@ -0,0 +1,45 @@
package com.ttstd.elderlyassistant.gson;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
public class NullStringToEmptyAdapterFactory<T> implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<T> rawType = (Class<T>) type.getRawType();
if (rawType != String.class) {
return null;
}
return (TypeAdapter<T>) new StringAdapter();
}
public static class StringAdapter extends TypeAdapter<String> {
@Override
public String read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return "";
}
return reader.nextString();
}
@Override
public void write(JsonWriter writer, String value) throws IOException {
if (value == null) {
writer.nullValue();
return;
}
writer.value(value);
}
}
}

View File

@@ -1,18 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.main.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<data>
</androidx.constraintlayout.widget.ConstraintLayout>
<variable
name="click"
type="com.ttstd.elderlyassistant.activity.main.MainActivity.BtnClick" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{click::wirteTag}"
android:text="写入标签"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -1,9 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.nfc.NfcActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
<data>
<variable
name="click"
type="com.ttstd.elderlyassistant.activity.nfc.NfcActivity.BtnClick" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/et_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Name"
android:inputType="text"
android:maxLines="1"
android:singleLine="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/et_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:hint="phone"
android:inputType="phone"
android:maxLines="1"
android:singleLine="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/et_name" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{click::write}"
android:text="写入"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_phone" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>