增加写入拨号,读取数据,优化无障碍
This commit is contained in:
@@ -1,15 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.ttstd.elderlyassistant">
|
||||
|
||||
<!--NFC权限-->
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<!--声明只有带NFC的手机才可以下载-->
|
||||
<!-- NFC权限 -->
|
||||
<uses-permission android:name="android.permission.NFC" /> <!-- 声明只有带NFC的手机才可以下载 -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.nfc"
|
||||
android:required="true" />
|
||||
|
||||
|
||||
<application
|
||||
android:name=".base.BaseApplication"
|
||||
android:allowBackup="true"
|
||||
@@ -28,7 +25,11 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".activity.setting.SettingsActivity" />
|
||||
<activity
|
||||
android:name=".activity.write.WriteMessageActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop" />
|
||||
<activity
|
||||
android:name=".activity.nfc.NfcActivity"
|
||||
android:exported="true"
|
||||
@@ -46,10 +47,11 @@
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<data android:mimeType="vnd.android.cursor.item/ndef_msg" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<service
|
||||
|
||||
@@ -64,14 +64,15 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
private static final String DIALER_HANDS_FREE_TEXT = "免提";
|
||||
private static final String DIALER_HANDS_FREE_CLOSE_TEXT = "免提,已关闭";
|
||||
|
||||
public static final int TYPE_VOICE = 0;
|
||||
public static final int TYPE_VIDEO = 1;
|
||||
public static final int TYPE_VOICE = 2;
|
||||
public static final int TYPE_VIDEO = 3;
|
||||
|
||||
private static final int WAIT_TIME = 1500;
|
||||
|
||||
private int mCallType = TYPE_VOICE;
|
||||
|
||||
private WechatContact mContact;
|
||||
private int mCallType = TYPE_VOICE;
|
||||
|
||||
private Step mCurrentStep = Step.WAITING;
|
||||
private String mName = "";//微信昵称
|
||||
private boolean mAutoAccept = false;
|
||||
@@ -114,10 +115,15 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
if (intent != null) {
|
||||
mContact = (WechatContact) intent.getSerializableExtra("WechatInfo");
|
||||
Log.e(TAG, "onStartCommand: wechatInfo = " + mContact);
|
||||
mCallType = intent.getIntExtra("call_type", TYPE_VOICE);
|
||||
mName = mContact.getName();
|
||||
mCurrentStep = Step.CLICK_HOME;
|
||||
launchWeChat();
|
||||
if (mContact != null) {
|
||||
mName = mContact.getName();
|
||||
mCallType = mContact.getOperation();
|
||||
mCurrentStep = Step.CLICK_HOME;
|
||||
launchWeChat();
|
||||
} else {
|
||||
Toaster.showLong("没有获取到联系人数据");
|
||||
}
|
||||
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
@@ -819,7 +825,6 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
DESCRIPTION
|
||||
}
|
||||
|
||||
public static final String SETTING_CALL_TYPE_ACTION = "setting_call_type_action";
|
||||
public static final String SETTING_AUTOMATIC_ANSWER_ACTION = "setting_automatic_answer_action";
|
||||
|
||||
private SettingBroadcastReceiver mSettingBroadcastReceiver;
|
||||
@@ -829,7 +834,6 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
mSettingBroadcastReceiver = new SettingBroadcastReceiver();
|
||||
}
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(SETTING_CALL_TYPE_ACTION);
|
||||
filter.addAction(SETTING_AUTOMATIC_ANSWER_ACTION);
|
||||
registerReceiver(mSettingBroadcastReceiver, filter);
|
||||
}
|
||||
@@ -841,11 +845,6 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
Log.e("SettingReceiver", "onReceive: " + action);
|
||||
if (TextUtils.isEmpty(action)) return;
|
||||
switch (action) {
|
||||
case SETTING_CALL_TYPE_ACTION:
|
||||
int callType = intent.getIntExtra("call_type", TYPE_VOICE);
|
||||
mCallType = callType;
|
||||
Log.e("SettingReceiver", "onReceive: callType = " + callType);
|
||||
break;
|
||||
case SETTING_AUTOMATIC_ANSWER_ACTION:
|
||||
boolean autoAnswer = intent.getBooleanExtra("auto_answer", false);
|
||||
mAutoAccept = autoAnswer;
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.ttstd.elderlyassistant.R;
|
||||
import com.ttstd.elderlyassistant.activity.setting.SettingsActivity;
|
||||
import com.ttstd.elderlyassistant.bean.WechatContact;
|
||||
import com.ttstd.elderlyassistant.databinding.ActivityMainBinding;
|
||||
import com.ttstd.elderlyassistant.utils.AccessibilityUtils;
|
||||
@@ -188,5 +189,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
public void openAccessibility(View view) {
|
||||
AccessibilityUtils.openAccessibilitySettings(MainActivity.this);
|
||||
}
|
||||
|
||||
public void openSettings(View view) {
|
||||
startActivity(new Intent(MainActivity.this, SettingsActivity.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.ttstd.elderlyassistant.activity.setting;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import com.ttstd.elderlyassistant.R;
|
||||
import com.ttstd.elderlyassistant.activity.write.WriteMessageActivity;
|
||||
import com.ttstd.elderlyassistant.databinding.ActivitySettingsBinding;
|
||||
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
private ActivitySettingsBinding mBinding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// setContentView(R.layout.activity_settings);
|
||||
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_settings);
|
||||
mBinding.setClick(new BtnClick());
|
||||
}
|
||||
|
||||
|
||||
public class BtnClick {
|
||||
|
||||
public void openWriteSettings(View view) {
|
||||
startActivity(new Intent(SettingsActivity.this, WriteMessageActivity.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package com.ttstd.elderlyassistant.activity.write;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.nfc.NdefMessage;
|
||||
import android.nfc.NdefRecord;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.nfc.Tag;
|
||||
import android.nfc.tech.Ndef;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.ttstd.elderlyassistant.R;
|
||||
import com.ttstd.elderlyassistant.databinding.ActivityWriteMessageBinding;
|
||||
import com.ttstd.elderlyassistant.utils.HexUtils;
|
||||
import com.ttstd.elderlyassistant.utils.WriteMessageUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class WriteMessageActivity extends AppCompatActivity {
|
||||
private static final String TAG = "WriteMessageActivity";
|
||||
|
||||
private ActivityWriteMessageBinding mBinding;
|
||||
|
||||
private NfcAdapter mNfcAdapter;
|
||||
private PendingIntent mPendingIntent;
|
||||
private Tag mTag;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// setContentView(R.layout.activity_write_message);
|
||||
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_write_message);
|
||||
mBinding.setClick(new BtnClick());
|
||||
|
||||
// 获取NFC适配器实例
|
||||
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
|
||||
if (mNfcAdapter == null) {
|
||||
Toast.makeText(this, "设备不支持NFC", Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
Log.e(TAG, "onStart: ");
|
||||
|
||||
//此处adapter需要重新获取,否则无法获取message
|
||||
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
|
||||
//一旦截获NFC消息,就会通过PendingIntent调用窗口
|
||||
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
Log.e(TAG, "onResume: ");
|
||||
|
||||
// 启用前台调度,实时监听NFC标签
|
||||
if (mNfcAdapter != null) {
|
||||
/*TAG: Tech [android.nfc.tech.NfcA, android.nfc.tech.MifareUltralight, android.nfc.tech.Ndef]*/
|
||||
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
Log.e(TAG, "onPause: ");
|
||||
|
||||
// 禁用前台调度,减少资源消耗
|
||||
if (mNfcAdapter != null) {
|
||||
mNfcAdapter.disableForegroundDispatch(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
String action = intent.getAction();
|
||||
Log.e(TAG, "onNewIntent: action = " + action);
|
||||
if (TextUtils.isEmpty(action)) return;
|
||||
switch (action) {
|
||||
case NfcAdapter.ACTION_NDEF_DISCOVERED:
|
||||
case NfcAdapter.ACTION_TAG_DISCOVERED:
|
||||
//1.获取Tag对象
|
||||
mTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
|
||||
if (mTag != null) {
|
||||
//2.获取Ndef的实例
|
||||
Ndef ndef = Ndef.get(mTag);
|
||||
String id = HexUtils.bytesToHexString(mTag.getId());
|
||||
mBinding.tvId.setText("已连接: " + id);
|
||||
Log.e(TAG, "onNewIntent: id = " + id);
|
||||
if (ndef != null) {
|
||||
Log.e(TAG, "onNewIntent: type = " + ndef.getType());
|
||||
Log.e(TAG, "onNewIntent: MaxSize = " + ndef.getMaxSize());
|
||||
parseNdefData(mTag);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// 解析 NDEF 数据
|
||||
private void parseNdefData(Tag tag) {
|
||||
Ndef ndef = Ndef.get(tag);
|
||||
if (ndef != null) {
|
||||
try {
|
||||
ndef.connect();
|
||||
NdefMessage ndefMessage = ndef.getNdefMessage();
|
||||
if (ndefMessage != null) {
|
||||
// 提取第一条记录的文本
|
||||
NdefRecord record = ndefMessage.getRecords()[0];
|
||||
String payload = new String(record.getPayload(), StandardCharsets.UTF_8);
|
||||
Log.e("NFC", "NDEF Text: " + payload.substring(1)); // 跳过编码头
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("NFC", "NDEF解析失败" + e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
ndef.close();
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BtnClick {
|
||||
public void write(View view) {
|
||||
if (mTag == null) {
|
||||
Toaster.showLong("NFC卡片未连接");
|
||||
return;
|
||||
}
|
||||
Editable editable = mBinding.etPhone.getText();
|
||||
if (TextUtils.isEmpty(editable)) {
|
||||
Toaster.showLong("请输入电话");
|
||||
return;
|
||||
}
|
||||
WriteMessageUtils.writePhoneToTag(mTag, editable.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -56,6 +56,14 @@ public class WechatContact implements Serializable {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public int getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
public void setOperation(int operation) {
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ttstd.elderlyassistant.utils;
|
||||
|
||||
public class HexUtils {
|
||||
|
||||
// 字节数组转16进制字符串(优化版)
|
||||
public static String bytesToHexString(byte[] bytes) {
|
||||
if (bytes == null) return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
sb.append(String.format("%02X", b)); // 直接格式化为大写16进制
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.ttstd.elderlyassistant.utils;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.nfc.FormatException;
|
||||
import android.nfc.NdefMessage;
|
||||
import android.nfc.NdefRecord;
|
||||
import android.nfc.Tag;
|
||||
import android.nfc.tech.Ndef;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class WriteMessageUtils {
|
||||
public static void writePhoneToTag(Tag tag, String phoneNumber) {
|
||||
// 构建tel:协议的URI
|
||||
Uri uri = Uri.parse("tel:" + phoneNumber);
|
||||
// 创建URI类型的NdefRecord
|
||||
NdefRecord uriRecord = NdefRecord.createUri(uri);
|
||||
NdefMessage message = new NdefMessage(new NdefRecord[]{uriRecord});
|
||||
|
||||
try {
|
||||
Ndef ndef = Ndef.get(tag);
|
||||
ndef.connect();
|
||||
ndef.writeNdefMessage(message); // 写入NDEF消息
|
||||
ndef.close();
|
||||
} catch (IOException | FormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeUrlToTag(Tag tag, String url) {
|
||||
// 确保URL包含协议(如http://)
|
||||
Uri uri = Uri.parse(url);
|
||||
NdefRecord uriRecord = NdefRecord.createUri(uri);
|
||||
NdefMessage message = new NdefMessage(new NdefRecord[]{uriRecord});
|
||||
|
||||
try {
|
||||
Ndef ndef = Ndef.get(tag);
|
||||
ndef.connect();
|
||||
ndef.writeNdefMessage(message);
|
||||
ndef.close();
|
||||
} catch (IOException | FormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,17 +16,34 @@
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{click::openAccessibility}"
|
||||
android:text="打开无障碍权限"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{click::openAccessibility}"
|
||||
android:text="打开无障碍权限"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{click::openSettings}"
|
||||
android:text="设置"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
38
app/src/main/res/layout/activity_settings.xml
Normal file
38
app/src/main/res/layout/activity_settings.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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"
|
||||
tools:context=".activity.setting.SettingsActivity">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="click"
|
||||
type="com.ttstd.elderlyassistant.activity.setting.SettingsActivity.BtnClick" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{click::openWriteSettings}"
|
||||
android:text="写入数据"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
56
app/src/main/res/layout/activity_write_message.xml
Normal file
56
app/src/main/res/layout/activity_write_message.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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"
|
||||
tools:context=".activity.write.WriteMessageActivity">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="click"
|
||||
type="com.ttstd.elderlyassistant.activity.write.WriteMessageActivity.BtnClick" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_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_toTopOf="parent" />
|
||||
|
||||
<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" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_id"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
Reference in New Issue
Block a user