优化通过scheme直接拨号和视频,增加数据加密。优化ContactInfo数据结构。优化无障碍流程
This commit is contained in:
@@ -1,8 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.ttstd.elderlyassistant">
|
package="com.ttstd.elderlyassistant">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||||
|
<uses-permission android:name="android.permission.SEND_SMS" />
|
||||||
<!-- NFC权限 -->
|
<!-- NFC权限 -->
|
||||||
<uses-permission android:name="android.permission.NFC" /> <!-- 声明只有带NFC的手机才可以下载 -->
|
<uses-permission android:name="android.permission.NFC" /> <!-- 声明只有带NFC的手机才可以下载 -->
|
||||||
|
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.nfc"
|
android:name="android.hardware.nfc"
|
||||||
android:required="true" />
|
android:required="true" />
|
||||||
@@ -53,16 +57,31 @@
|
|||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<!-- <activity-->
|
|
||||||
<!-- android:name=".activity.third_party.NfcQQMusicActivity"-->
|
<!-- <activity -->
|
||||||
<!-- android:launchMode="singleTask">-->
|
<!-- android:name=".activity.third_party.NfcQQMusicActivity" -->
|
||||||
<!-- <intent-filter>-->
|
<!-- android:launchMode="singleTask"> -->
|
||||||
<!-- <action android:name="android.nfc.action.NDEF_DISCOVERED" />-->
|
<!-- <intent-filter> -->
|
||||||
<!-- <category android:name="android.intent.category.DEFAULT" />-->
|
<!-- <action android:name="android.nfc.action.NDEF_DISCOVERED" /> -->
|
||||||
<!-- <!– 匹配 QQ 音乐 Scheme –>-->
|
<!-- <category android:name="android.intent.category.DEFAULT" /> -->
|
||||||
<!-- <data android:scheme="qqmusic" />-->
|
<!-- <!– 匹配 QQ 音乐 Scheme –> -->
|
||||||
<!-- </intent-filter>-->
|
<!-- <data android:scheme="qqmusic" /> -->
|
||||||
<!-- </activity>-->
|
<!-- </intent-filter> -->
|
||||||
|
<!-- </activity> -->
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".activity.scheme.SchemeActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<!-- 指定 Scheme 协议 -->
|
||||||
|
<data android:scheme="elderlyassistant" android:host="ttstd.com" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name="com.google.android.accessibility.selecttospeak.SelectToSpeakService"
|
android:name="com.google.android.accessibility.selecttospeak.SelectToSpeakService"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import com.hjq.toast.Toaster;
|
import com.hjq.toast.Toaster;
|
||||||
import com.tencent.mmkv.MMKV;
|
import com.tencent.mmkv.MMKV;
|
||||||
import com.ttstd.elderlyassistant.bean.WechatContact;
|
import com.ttstd.elderlyassistant.bean.ContactInfo;
|
||||||
import com.ttstd.elderlyassistant.config.CommonConfig;
|
import com.ttstd.elderlyassistant.config.CommonConfig;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -39,7 +39,7 @@ import io.reactivex.rxjava3.disposables.Disposable;
|
|||||||
import io.reactivex.rxjava3.functions.Consumer;
|
import io.reactivex.rxjava3.functions.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过微信标签最高支持8.0.49,8.0.50 获取不到数据
|
* 通过微信标签最高支持8.0.49
|
||||||
* 8.0.54 可以获取
|
* 8.0.54 可以获取
|
||||||
* 通过 {@link AccessibilityService#getWindows}和修改accessibility-service 配置能遍历屏幕元素
|
* 通过 {@link AccessibilityService#getWindows}和修改accessibility-service 配置能遍历屏幕元素
|
||||||
*/
|
*/
|
||||||
@@ -56,8 +56,8 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
private static final String TAG_TEXT = "标签";
|
private static final String TAG_TEXT = "标签";
|
||||||
private static final String PARENT_VIDEO_TEXT = "视频通话";
|
private static final String PARENT_VIDEO_TEXT = "视频通话";
|
||||||
|
|
||||||
|
private static final String VOICE_TEXT = "语音通话";
|
||||||
private static final String VIDEO_TEXT = "视频通话";
|
private static final String VIDEO_TEXT = "视频通话";
|
||||||
private static final String CALL_TEXT = "语音通话";
|
|
||||||
|
|
||||||
private static final String RECEIVE_DESCRIPTION = "接听";
|
private static final String RECEIVE_DESCRIPTION = "接听";
|
||||||
private static final String HANDS_FREE_TEXT = "扬声器已关";
|
private static final String HANDS_FREE_TEXT = "扬声器已关";
|
||||||
@@ -65,17 +65,19 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
private static final String DIALER_HANDS_FREE_TEXT = "免提";
|
private static final String DIALER_HANDS_FREE_TEXT = "免提";
|
||||||
private static final String DIALER_HANDS_FREE_CLOSE_TEXT = "免提,已关闭";
|
private static final String DIALER_HANDS_FREE_CLOSE_TEXT = "免提,已关闭";
|
||||||
|
|
||||||
public static final int TYPE_VOICE = 2;
|
public static final int TYPE_VOICE = 3;
|
||||||
public static final int TYPE_VIDEO = 3;
|
public static final int TYPE_VIDEO = 4;
|
||||||
|
|
||||||
private static final int WAIT_TIME = 1500;
|
private static final int WAIT_TIME = 1500;
|
||||||
|
|
||||||
private WechatContact mContact;
|
private ContactInfo mContact;
|
||||||
private String mName = "";//微信昵称
|
/**
|
||||||
|
* 微信昵称
|
||||||
|
*/
|
||||||
|
private String mName = "";
|
||||||
private int mCallType = TYPE_VOICE;
|
private int mCallType = TYPE_VOICE;
|
||||||
|
|
||||||
private Step mCurrentStep = Step.WAITING;
|
private Procedure mCurrentProcedure = Procedure.WAITING;
|
||||||
private boolean mAutoAccept = false;
|
private boolean mAutoAccept = false;
|
||||||
|
|
||||||
private Disposable mDisposable;
|
private Disposable mDisposable;
|
||||||
@@ -92,11 +94,11 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
Log.e(TAG, "onCreate: ");
|
Log.e(TAG, "onCreate: ");
|
||||||
registerSettingReceiver();
|
registerSettingReceiver();
|
||||||
mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_CALL_AUTO_ACCEPT_KEY, false);
|
mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_CALL_AUTO_ACCEPT_KEY, false);
|
||||||
dealingAccessibilityEvent();
|
|
||||||
|
|
||||||
|
initDisposable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dealingAccessibilityEvent() {
|
private void initDisposable() {
|
||||||
mDisposable = Observable.create(new ObservableOnSubscribe<AccessibilityEvent>() {
|
mDisposable = Observable.create(new ObservableOnSubscribe<AccessibilityEvent>() {
|
||||||
@Override
|
@Override
|
||||||
public void subscribe(@NonNull ObservableEmitter<AccessibilityEvent> emitter) throws Throwable {
|
public void subscribe(@NonNull ObservableEmitter<AccessibilityEvent> emitter) throws Throwable {
|
||||||
@@ -106,23 +108,23 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
.subscribe(new Consumer<AccessibilityEvent>() {
|
.subscribe(new Consumer<AccessibilityEvent>() {
|
||||||
@Override
|
@Override
|
||||||
public void accept(AccessibilityEvent accessibilityEvent) throws Throwable {
|
public void accept(AccessibilityEvent accessibilityEvent) throws Throwable {
|
||||||
Log.e(TAG, "accept: ");
|
Log.e(TAG, "initDisposable accept: ");
|
||||||
_onAccessibilityEvent(accessibilityEvent);
|
parseAccessibilityEvent(accessibilityEvent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Log.e(TAG, "dealingAccessibilityEvent: mDisposable hash = " + mDisposable.hashCode());
|
Log.e(TAG, "initDisposable: mDisposable hash = " + mDisposable.hashCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
Log.e(TAG, "onStartCommand: ");
|
Log.e(TAG, "onStartCommand: ");
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
mContact = (WechatContact) intent.getSerializableExtra("WechatInfo");
|
mContact = (ContactInfo) intent.getSerializableExtra("WechatInfo");
|
||||||
Log.e(TAG, "onStartCommand: wechatInfo = " + mContact);
|
Log.e(TAG, "onStartCommand: wechatInfo = " + mContact);
|
||||||
if (mContact != null) {
|
if (mContact != null) {
|
||||||
mName = mContact.getName();
|
mName = mContact.getName();
|
||||||
mCallType = mContact.getOperation();
|
mCallType = mContact.getOperation();
|
||||||
mCurrentStep = Step.CLICK_HOME;
|
mCurrentProcedure = Procedure.CLICK_HOME;
|
||||||
launchWeChat();
|
launchWeChat();
|
||||||
} else {
|
} else {
|
||||||
Toaster.showLong("没有获取到联系人数据");
|
Toaster.showLong("没有获取到联系人数据");
|
||||||
@@ -155,12 +157,12 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkClassName(AccessibilityEvent event) {
|
private void checkClassName(AccessibilityEvent event) {
|
||||||
Log.e(TAG, "checkClassName: mCurrentStep = " + mCurrentStep);
|
Log.e(TAG, "checkClassName: mCurrentStep = " + mCurrentProcedure);
|
||||||
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
|
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
|
||||||
String currentPackageName = event.getPackageName().toString();
|
String currentPackageName = event.getPackageName().toString();
|
||||||
String currentClassName = event.getClassName().toString();
|
String currentClassName = event.getClassName().toString();
|
||||||
|
|
||||||
switch (mCurrentStep) {
|
switch (mCurrentProcedure) {
|
||||||
case WAITING:
|
case WAITING:
|
||||||
if (!TextUtils.isEmpty(currentPackageName) && "com.android.incallui".equals(currentPackageName)) {
|
if (!TextUtils.isEmpty(currentPackageName) && "com.android.incallui".equals(currentPackageName)) {
|
||||||
Log.e(TAG, "checkClassName: to dialer hands free");
|
Log.e(TAG, "checkClassName: to dialer hands free");
|
||||||
@@ -178,7 +180,7 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
case "com.tencent.mm.plugin.account.ui.WelcomeActivity":
|
case "com.tencent.mm.plugin.account.ui.WelcomeActivity":
|
||||||
case "com.tencent.mm.plugin.account.ui.LoginPasswordUI":
|
case "com.tencent.mm.plugin.account.ui.LoginPasswordUI":
|
||||||
Toaster.showLong("请先登录微信");
|
Toaster.showLong("请先登录微信");
|
||||||
mCurrentStep = Step.WAITING;
|
mCurrentProcedure = Procedure.WAITING;
|
||||||
break;
|
break;
|
||||||
case "com.tencent.mm.plugin.label.ui.ContactLabelManagerUI":
|
case "com.tencent.mm.plugin.label.ui.ContactLabelManagerUI":
|
||||||
|
|
||||||
@@ -197,20 +199,20 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
*
|
*
|
||||||
* @param event
|
* @param event
|
||||||
*/
|
*/
|
||||||
private void _onAccessibilityEvent(AccessibilityEvent event) {
|
private void parseAccessibilityEvent(AccessibilityEvent event) {
|
||||||
Log.e(TAG, "_onAccessibilityEvent: " + mCurrentStep);
|
Log.e(TAG, "parseAccessibilityEvent: " + mCurrentProcedure);
|
||||||
switch (mCurrentStep) {
|
switch (mCurrentProcedure) {
|
||||||
case WAITING:
|
case WAITING:
|
||||||
mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_CALL_AUTO_ACCEPT_KEY, false);
|
mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_CALL_AUTO_ACCEPT_KEY, false);
|
||||||
Log.e(TAG, "_onAccessibilityEvent: mAutoAccept = " + mAutoAccept);
|
Log.e(TAG, "parseAccessibilityEvent: mAutoAccept = " + mAutoAccept);
|
||||||
if (!mAutoAccept) {
|
if (!mAutoAccept) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stepAnswer(Property.DESCRIPTION, RECEIVE_DESCRIPTION)) {
|
if (stepAnswer(Property.DESCRIPTION, RECEIVE_DESCRIPTION)) {
|
||||||
mCurrentStep = Step.WECHAT_HANDS_FREE;
|
mCurrentProcedure = Procedure.WECHAT_HANDS_FREE;
|
||||||
Toast.makeText(this, "已自动接听视频/语音", Toast.LENGTH_LONG).show();
|
Toast.makeText(this, "已自动接听视频/语音", Toast.LENGTH_LONG).show();
|
||||||
} else {
|
} else {
|
||||||
mCurrentStep = Step.WAITING;
|
mCurrentProcedure = Procedure.WAITING;
|
||||||
// clickAnswer();
|
// clickAnswer();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -221,29 +223,29 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
if (findHandsFree(Property.DESCRIPTION, DIALER_HANDS_FREE_CLOSE_TEXT)) {
|
if (findHandsFree(Property.DESCRIPTION, DIALER_HANDS_FREE_CLOSE_TEXT)) {
|
||||||
dialerHandsFree(Property.TEXT, DIALER_HANDS_FREE_TEXT);
|
dialerHandsFree(Property.TEXT, DIALER_HANDS_FREE_TEXT);
|
||||||
} else {
|
} else {
|
||||||
mCurrentStep = Step.WAITING;
|
mCurrentProcedure = Procedure.WAITING;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CLICK_HOME://主页能找到直接点击进去更多
|
case CLICK_HOME://主页能找到直接点击进去更多
|
||||||
if (stepHome(Property.TEXT, mName)) {
|
if (stepHome(Property.TEXT, mName)) {
|
||||||
Log.e(TAG, "_onAccessibilityEvent: not found contact in home");
|
Log.e(TAG, "parseAccessibilityEvent: not found contact in home");
|
||||||
} else {
|
} else {
|
||||||
clickViewById("com.tencent.mm:id/jha", Step.CLICK_SEARCH);
|
clickViewById("com.tencent.mm:id/jha", Procedure.CLICK_SEARCH);
|
||||||
// step(Property.DESCRIPTION, SEARCH_TEXT, Step.CLICK_SEARCH);
|
// step(Property.DESCRIPTION, SEARCH_TEXT, Step.CLICK_SEARCH);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CLICK_SEARCH:
|
case CLICK_SEARCH:
|
||||||
putString(mName, Step.CLICK_SEARCH_CONTACT);
|
putString(mName, Procedure.CLICK_SEARCH_CONTACT);
|
||||||
break;
|
break;
|
||||||
case CLICK_SEARCH_CONTACT:
|
case CLICK_SEARCH_CONTACT:
|
||||||
if (findSearchContact(Step.FIND_CONTACT)) {
|
if (findSearchContact(Procedure.FIND_CONTACT)) {
|
||||||
findSearchContact(Property.TEXT, mName, Step.CLICK_QUICK_WECHAT_CALL);
|
findSearchContact(Property.TEXT, mName, Procedure.CLICK_QUICK_WECHAT_CALL);
|
||||||
} else {
|
} else {
|
||||||
Toaster.show("没有找到联系人");
|
Toaster.show("没有找到联系人");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CLICK_QUICK_WECHAT_CALL://点击更多页面
|
case CLICK_QUICK_WECHAT_CALL://点击更多页面
|
||||||
clickViewById("com.tencent.mm:id/bjz", Step.CLICK_TARGET);
|
clickViewById("com.tencent.mm:id/bjz", Procedure.CLICK_TARGET);
|
||||||
// step(Property.DESCRIPTION, MORE_NAME, Step.CLICK_TARGET);
|
// step(Property.DESCRIPTION, MORE_NAME, Step.CLICK_TARGET);
|
||||||
break;
|
break;
|
||||||
case CLICK_TARGET://点击视频通话
|
case CLICK_TARGET://点击视频通话
|
||||||
@@ -252,40 +254,34 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CLICK_CONTACT://进入通讯录界面
|
case CLICK_CONTACT://进入通讯录界面
|
||||||
if (stepHome(Property.TEXT, CONTACT_TEXT, Step.FIND_TAG)) {
|
if (stepHome(Property.TEXT, CONTACT_TEXT, Procedure.FIND_TAG)) {
|
||||||
Log.e(TAG, "_onAccessibilityEvent: enter contact");
|
Log.e(TAG, "parseAccessibilityEvent: enter contact");
|
||||||
} else {
|
} else {
|
||||||
touchContact();
|
touchContact();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FIND_CONTACT://模拟滑动找到联系人
|
case FIND_CONTACT://模拟滑动找到联系人
|
||||||
findSearchContact(Property.TEXT, mName, Step.CLICK_QUICK_WECHAT_CALL);
|
findSearchContact(Property.TEXT, mName, Procedure.CLICK_QUICK_WECHAT_CALL);
|
||||||
break;
|
break;
|
||||||
case FIND_TAG:
|
case FIND_TAG:
|
||||||
step(Property.TEXT, TAG_TEXT, Step.CLICK_TAG);
|
step(Property.TEXT, TAG_TEXT, Procedure.CLICK_TAG);
|
||||||
break;
|
break;
|
||||||
case CLICK_TAG:
|
case CLICK_TAG:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case CLICK_NAME://点击item
|
case CLICK_NAME://点击item
|
||||||
findContact(Property.TEXT, mName, Step.CLICK_INFO);
|
findContact(Property.TEXT, mName, Procedure.CLICK_INFO);
|
||||||
break;
|
break;
|
||||||
case CLICK_INFO://进入个人信息页面
|
case CLICK_INFO://进入个人信息页面
|
||||||
stepCallDialog(Property.TEXT, DIALER_TEXT, Step.CLICK_CALL);
|
stepCallDialog(Property.TEXT, DIALER_TEXT, Procedure.CLICK_CALL);
|
||||||
break;
|
break;
|
||||||
case CLICK_CALL://打视频或者电话
|
case CLICK_CALL://打视频或者电话
|
||||||
if (mCallType == TYPE_VIDEO) {
|
if (mCallType == TYPE_VIDEO) {
|
||||||
step(Property.TEXT, VIDEO_TEXT, Step.WAITING);
|
step(Property.TEXT, VIDEO_TEXT, Procedure.WAITING);
|
||||||
} else if (mCallType == TYPE_VOICE) {
|
} else if (mCallType == TYPE_VOICE) {
|
||||||
step(Property.TEXT, CALL_TEXT, Step.WAITING);
|
step(Property.TEXT, VOICE_TEXT, Procedure.WAITING);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// case CLICK_VIDEO_CALL:
|
|
||||||
// if (step(Property.TEXT, VIDEO_TEXT)) {
|
|
||||||
// Log.d(TAG, "finish, now: " + mCurrentStep);
|
|
||||||
// Toast.makeText(this, "成功发起视频聊天", Toast.LENGTH_LONG).show();
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -320,7 +316,7 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean step(Property type, String text, Step nextStep) {
|
private boolean step(Property type, String text, Procedure nextProcedure) {
|
||||||
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
Rect rect = new Rect();
|
Rect rect = new Rect();
|
||||||
@@ -330,9 +326,9 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
clickNode(node);
|
clickNode(node);
|
||||||
Log.e(TAG, "step: mCurrentStep: " + mCurrentStep + " done");
|
Log.e(TAG, "step: mCurrentStep: " + mCurrentProcedure + " done");
|
||||||
mCurrentStep = nextStep;
|
mCurrentProcedure = nextProcedure;
|
||||||
Log.e(TAG, "step: next: " + mCurrentStep);
|
Log.e(TAG, "step: next: " + mCurrentProcedure);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -340,7 +336,7 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 2025/2/8 先把通讯录点击的换成node
|
// TODO: 2025/2/8 先把通讯录点击的换成node
|
||||||
private boolean stepHome(Property type, String text, Step nextStep) {
|
private boolean stepHome(Property type, String text, Procedure nextProcedure) {
|
||||||
AccessibilityNodeInfo node = findNode(getWindows(), type, text);
|
AccessibilityNodeInfo node = findNode(getWindows(), type, text);
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
Rect rect = new Rect();
|
Rect rect = new Rect();
|
||||||
@@ -350,9 +346,9 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
clickNode(node);
|
clickNode(node);
|
||||||
Log.e(TAG, "step: mCurrentStep: " + mCurrentStep + " done");
|
Log.e(TAG, "step: mCurrentStep: " + mCurrentProcedure + " done");
|
||||||
mCurrentStep = nextStep;
|
mCurrentProcedure = nextProcedure;
|
||||||
Log.e(TAG, "step: next: " + mCurrentStep);
|
Log.e(TAG, "step: next: " + mCurrentProcedure);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -363,35 +359,35 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
private void touchContact() {
|
private void touchContact() {
|
||||||
boolean successful = clickByPoint(268, 1440);
|
boolean successful = clickByPoint(268, 1440);
|
||||||
if (successful) {
|
if (successful) {
|
||||||
mCurrentStep = Step.FIND_TAG;
|
mCurrentProcedure = Procedure.FIND_TAG;
|
||||||
} else {
|
} else {
|
||||||
mCurrentStep = Step.WAITING;
|
mCurrentProcedure = Procedure.WAITING;
|
||||||
Toaster.show("点击失败,请重试");
|
Toaster.show("点击失败,请重试");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean findSearchContact(Step nextStep) {
|
private boolean findSearchContact(Procedure nextProcedure) {
|
||||||
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId("com.tencent.mm:id/gzf");
|
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId("com.tencent.mm:id/gzf");
|
||||||
Log.e(TAG, "findSearchContact: " + nodeInfos);
|
Log.e(TAG, "findSearchContact: " + nodeInfos);
|
||||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||||
return optional.isPresent();
|
return optional.isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findSearchContact(Property type, String text, Step nextStep) {
|
private void findSearchContact(Property type, String text, Procedure nextProcedure) {
|
||||||
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId("com.tencent.mm:id/odf");
|
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId("com.tencent.mm:id/odf");
|
||||||
Log.e(TAG, "findSearchContact: " + nodeInfos);
|
Log.e(TAG, "findSearchContact: " + nodeInfos);
|
||||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||||
if (optional.isPresent()) {
|
if (optional.isPresent()) {
|
||||||
AccessibilityNodeInfo nodeInfo = optional.get();
|
AccessibilityNodeInfo nodeInfo = optional.get();
|
||||||
clickNode(nodeInfo);
|
clickNode(nodeInfo);
|
||||||
mCurrentStep = nextStep;
|
mCurrentProcedure = nextProcedure;
|
||||||
} else {
|
} else {
|
||||||
Toaster.show("没有找到联系人");
|
Toaster.show("没有找到联系人");
|
||||||
mCurrentStep = Step.WAITING;
|
mCurrentProcedure = Procedure.WAITING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void putString(String text, Step nextStep) {
|
private void putString(String text, Procedure nextProcedure) {
|
||||||
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId("com.tencent.mm:id/d98");
|
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId("com.tencent.mm:id/d98");
|
||||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||||
if (optional.isPresent()) {
|
if (optional.isPresent()) {
|
||||||
@@ -405,20 +401,20 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
//see https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.AccessibilityAction#ACTION_IME_ENTER
|
//see https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.AccessibilityAction#ACTION_IME_ENTER
|
||||||
nodeInfo.performAction(ACTION_IME_ENTER_ID);
|
nodeInfo.performAction(ACTION_IME_ENTER_ID);
|
||||||
}
|
}
|
||||||
mCurrentStep = nextStep;
|
mCurrentProcedure = nextProcedure;
|
||||||
} else {
|
} else {
|
||||||
Toaster.show("没有找到搜索框");
|
Toaster.show("没有找到搜索框");
|
||||||
mCurrentStep = Step.WAITING;
|
mCurrentProcedure = Procedure.WAITING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clickViewById(String id, Step nextStep) {
|
private void clickViewById(String id, Procedure nextProcedure) {
|
||||||
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId(id);
|
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId(id);
|
||||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||||
if (optional.isPresent()) {
|
if (optional.isPresent()) {
|
||||||
AccessibilityNodeInfo nodeInfo = optional.get();
|
AccessibilityNodeInfo nodeInfo = optional.get();
|
||||||
clickNode(nodeInfo);
|
clickNode(nodeInfo);
|
||||||
mCurrentStep = nextStep;
|
mCurrentProcedure = nextProcedure;
|
||||||
} else {
|
} else {
|
||||||
Toaster.show("没有找到搜索按钮");
|
Toaster.show("没有找到搜索按钮");
|
||||||
}
|
}
|
||||||
@@ -448,15 +444,15 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean stepCallDialog(Property type, String text, Step nextStep) {
|
private boolean stepCallDialog(Property type, String text, Procedure nextProcedure) {
|
||||||
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
Log.e(TAG, "stepCallDialog: isVisibleToUser: " + node.isVisibleToUser());
|
Log.e(TAG, "stepCallDialog: isVisibleToUser: " + node.isVisibleToUser());
|
||||||
if (node.isVisibleToUser()) {
|
if (node.isVisibleToUser()) {
|
||||||
clickNode(node);
|
clickNode(node);
|
||||||
Log.e(TAG, "stepCallDialog: mCurrentStep: " + mCurrentStep + " done");
|
Log.e(TAG, "stepCallDialog: mCurrentStep: " + mCurrentProcedure + " done");
|
||||||
mCurrentStep = nextStep;
|
mCurrentProcedure = nextProcedure;
|
||||||
Log.e(TAG, "stepCallDialog: next: " + mCurrentStep);
|
Log.e(TAG, "stepCallDialog: next: " + mCurrentProcedure);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
scrollDown();
|
scrollDown();
|
||||||
@@ -466,7 +462,7 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
if (mFindCount == mMaxCount) {
|
if (mFindCount == mMaxCount) {
|
||||||
Log.e("stepCallDialog", "mCurrentStep: max");
|
Log.e("stepCallDialog", "mCurrentStep: max");
|
||||||
Toast.makeText(this, "没有找到联系人", Toast.LENGTH_LONG).show();
|
Toast.makeText(this, "没有找到联系人", Toast.LENGTH_LONG).show();
|
||||||
mCurrentStep = Step.WAITING;
|
mCurrentProcedure = Procedure.WAITING;
|
||||||
mFindCount = 0;
|
mFindCount = 0;
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@@ -483,12 +479,12 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
clickNode(node);
|
clickNode(node);
|
||||||
Log.e(TAG, "stepHome: mCurrentStep: " + mCurrentStep + " done");
|
Log.e(TAG, "stepHome: mCurrentStep: " + mCurrentProcedure + " done");
|
||||||
mCurrentStep = Step.CLICK_QUICK_WECHAT_CALL;
|
mCurrentProcedure = Procedure.CLICK_QUICK_WECHAT_CALL;
|
||||||
Log.e(TAG, "stepHome: next: " + mCurrentStep);
|
Log.e(TAG, "stepHome: next: " + mCurrentProcedure);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
mCurrentStep = Step.CLICK_SEARCH;
|
mCurrentProcedure = Procedure.CLICK_SEARCH;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -497,20 +493,20 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
private int mMaxCount = 5;
|
private int mMaxCount = 5;
|
||||||
|
|
||||||
|
|
||||||
private boolean findContact(Property type, String text, Step nextStep) {
|
private boolean findContact(Property type, String text, Procedure nextProcedure) {
|
||||||
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
clickNode(node);
|
clickNode(node);
|
||||||
Log.e("findContact", "mCurrentStep: " + mCurrentStep + " done");
|
Log.e("findContact", "mCurrentStep: " + mCurrentProcedure + " done");
|
||||||
mCurrentStep = nextStep;
|
mCurrentProcedure = nextProcedure;
|
||||||
Log.e("findContact", "next: " + mCurrentStep);
|
Log.e("findContact", "next: " + mCurrentProcedure);
|
||||||
mFindCount = 0;
|
mFindCount = 0;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (mFindCount == mMaxCount) {
|
if (mFindCount == mMaxCount) {
|
||||||
Log.e("findContact", "mCurrentStep: max");
|
Log.e("findContact", "mCurrentStep: max");
|
||||||
Toast.makeText(this, "没有找到联系人", Toast.LENGTH_LONG).show();
|
Toast.makeText(this, "没有找到联系人", Toast.LENGTH_LONG).show();
|
||||||
mCurrentStep = Step.WAITING;
|
mCurrentProcedure = Procedure.WAITING;
|
||||||
mFindCount = 0;
|
mFindCount = 0;
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@@ -620,9 +616,9 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
Log.e(TAG, "stepCall: " + point);
|
Log.e(TAG, "stepCall: " + point);
|
||||||
clickByPoint(point.x, point.y);
|
clickByPoint(point.x, point.y);
|
||||||
// clickNode(node);
|
// clickNode(node);
|
||||||
Log.e(TAG, "stepCall: mCurrentStep " + mCurrentStep + " done");
|
Log.e(TAG, "stepCall: mCurrentStep " + mCurrentProcedure + " done");
|
||||||
mCurrentStep = Step.CLICK_CALL;
|
mCurrentProcedure = Procedure.CLICK_CALL;
|
||||||
Log.e(TAG, "stepCall: next " + mCurrentStep);
|
Log.e(TAG, "stepCall: next " + mCurrentProcedure);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "stepCall: not found");
|
Log.e(TAG, "stepCall: not found");
|
||||||
@@ -636,7 +632,7 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
if (accessibilityNodeInfo.isPresent()) {
|
if (accessibilityNodeInfo.isPresent()) {
|
||||||
AccessibilityNodeInfo nodeInfo = accessibilityNodeInfo.get();
|
AccessibilityNodeInfo nodeInfo = accessibilityNodeInfo.get();
|
||||||
clickNode(nodeInfo);
|
clickNode(nodeInfo);
|
||||||
mCurrentStep = Step.CLICK_CALL;
|
mCurrentProcedure = Procedure.CLICK_CALL;
|
||||||
} else {
|
} else {
|
||||||
Toaster.show("没有找到通话按钮");
|
Toaster.show("没有找到通话按钮");
|
||||||
}
|
}
|
||||||
@@ -650,9 +646,9 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
clickByPoint(point.x, point.y - 50);
|
clickByPoint(point.x, point.y - 50);
|
||||||
clickByPoint(point.x, point.y);
|
clickByPoint(point.x, point.y);
|
||||||
// clickNode(node);
|
// clickNode(node);
|
||||||
Log.e(TAG, "stepAnswer: mCurrentStep " + mCurrentStep + " done");
|
Log.e(TAG, "stepAnswer: mCurrentStep " + mCurrentProcedure + " done");
|
||||||
mCurrentStep = Step.WAITING;
|
mCurrentProcedure = Procedure.WAITING;
|
||||||
Log.e(TAG, "stepAnswer: next " + mCurrentStep);
|
Log.e(TAG, "stepAnswer: next " + mCurrentProcedure);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "stepAnswer: not found");
|
Log.e(TAG, "stepAnswer: not found");
|
||||||
@@ -667,9 +663,9 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
node.getBoundsInScreen(rect);
|
node.getBoundsInScreen(rect);
|
||||||
Log.e(TAG, "dialerHandsFree: rect = " + rect);
|
Log.e(TAG, "dialerHandsFree: rect = " + rect);
|
||||||
clickNode(node);
|
clickNode(node);
|
||||||
Log.e(TAG, "dialerHandsFree: mCurrentStep: " + mCurrentStep + " done");
|
Log.e(TAG, "dialerHandsFree: mCurrentStep: " + mCurrentProcedure + " done");
|
||||||
mCurrentStep = Step.WAITING;
|
mCurrentProcedure = Procedure.WAITING;
|
||||||
Log.e(TAG, "dialerHandsFree: next: " + mCurrentStep);
|
Log.e(TAG, "dialerHandsFree: next: " + mCurrentProcedure);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -695,13 +691,13 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
clickByPoint(point.x, point.y - 50);
|
clickByPoint(point.x, point.y - 50);
|
||||||
clickByPoint(point.x, point.y);
|
clickByPoint(point.x, point.y);
|
||||||
// clickNode(node);
|
// clickNode(node);
|
||||||
Log.e(TAG, "handsFree: mCurrentStep " + mCurrentStep + " done");
|
Log.e(TAG, "handsFree: mCurrentStep " + mCurrentProcedure + " done");
|
||||||
mCurrentStep = Step.WAITING;
|
mCurrentProcedure = Procedure.WAITING;
|
||||||
Log.e(TAG, "handsFree: next " + mCurrentStep);
|
Log.e(TAG, "handsFree: next " + mCurrentProcedure);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "handsFree: not found");
|
Log.e(TAG, "handsFree: not found");
|
||||||
mCurrentStep = Step.WAITING;
|
mCurrentProcedure = Procedure.WAITING;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -791,7 +787,7 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
return scrollScreen(0.5, 1.5);
|
return scrollScreen(0.5, 1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum Step {
|
private enum Procedure {
|
||||||
WAITING,
|
WAITING,
|
||||||
//微信免提
|
//微信免提
|
||||||
WECHAT_HANDS_FREE,
|
WECHAT_HANDS_FREE,
|
||||||
@@ -822,7 +818,7 @@ public class SelectToSpeakService extends AccessibilityService {
|
|||||||
CLICK_CALL,
|
CLICK_CALL,
|
||||||
CLICK_VIDEO_CALL;
|
CLICK_VIDEO_CALL;
|
||||||
|
|
||||||
private Step next() {
|
private Procedure next() {
|
||||||
return values()[(this.ordinal() + 1) % values().length];
|
return values()[(this.ordinal() + 1) % values().length];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import com.google.gson.reflect.TypeToken;
|
|||||||
import com.hjq.toast.Toaster;
|
import com.hjq.toast.Toaster;
|
||||||
import com.ttstd.elderlyassistant.R;
|
import com.ttstd.elderlyassistant.R;
|
||||||
import com.ttstd.elderlyassistant.activity.setting.SettingsActivity;
|
import com.ttstd.elderlyassistant.activity.setting.SettingsActivity;
|
||||||
import com.ttstd.elderlyassistant.bean.WechatContact;
|
import com.ttstd.elderlyassistant.bean.ContactInfo;
|
||||||
import com.ttstd.elderlyassistant.databinding.ActivityMainBinding;
|
import com.ttstd.elderlyassistant.databinding.ActivityMainBinding;
|
||||||
import com.ttstd.elderlyassistant.utils.AccessibilityUtils;
|
import com.ttstd.elderlyassistant.utils.AccessibilityUtils;
|
||||||
|
|
||||||
@@ -130,10 +130,10 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
Log.e(TAG, "readNfcTag: textRecord = " + textRecord);
|
Log.e(TAG, "readNfcTag: textRecord = " + textRecord);
|
||||||
Log.e(TAG, "readNfcTag: contentSize = " + contentSize);
|
Log.e(TAG, "readNfcTag: contentSize = " + contentSize);
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
Type type = new TypeToken<WechatContact>() {
|
Type type = new TypeToken<ContactInfo>() {
|
||||||
}.getType();
|
}.getType();
|
||||||
try {
|
try {
|
||||||
WechatContact contact = gson.fromJson(textRecord, type);
|
ContactInfo contact = gson.fromJson(textRecord, type);
|
||||||
Intent accessibilityIntent = new Intent(MainActivity.this, SelectToSpeakService.class);
|
Intent accessibilityIntent = new Intent(MainActivity.this, SelectToSpeakService.class);
|
||||||
accessibilityIntent.putExtra("WechatInfo", contact);
|
accessibilityIntent.putExtra("WechatInfo", contact);
|
||||||
startService(accessibilityIntent);
|
startService(accessibilityIntent);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import com.google.gson.Gson;
|
|||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.hjq.toast.Toaster;
|
import com.hjq.toast.Toaster;
|
||||||
import com.ttstd.elderlyassistant.R;
|
import com.ttstd.elderlyassistant.R;
|
||||||
import com.ttstd.elderlyassistant.bean.WechatContact;
|
import com.ttstd.elderlyassistant.bean.ContactInfo;
|
||||||
import com.ttstd.elderlyassistant.databinding.ActivityNfcBinding;
|
import com.ttstd.elderlyassistant.databinding.ActivityNfcBinding;
|
||||||
import com.ttstd.elderlyassistant.gson.GsonUtils;
|
import com.ttstd.elderlyassistant.gson.GsonUtils;
|
||||||
import com.ttstd.elderlyassistant.utils.NdefUtils;
|
import com.ttstd.elderlyassistant.utils.NdefUtils;
|
||||||
@@ -91,11 +91,11 @@ public class NfcActivity extends AppCompatActivity {
|
|||||||
if (!results.isEmpty()) {
|
if (!results.isEmpty()) {
|
||||||
String jsonString = results.get(0);
|
String jsonString = results.get(0);
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
Type type = new TypeToken<WechatContact>() {
|
Type type = new TypeToken<ContactInfo>() {
|
||||||
}.getType();
|
}.getType();
|
||||||
try {
|
try {
|
||||||
WechatContact contact = gson.fromJson(jsonString, type);
|
ContactInfo contact = gson.fromJson(jsonString, type);
|
||||||
mBinding.setWechatContact(contact);
|
mBinding.setContactInfo(contact);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "resolveIntent: " + e.getMessage());
|
Log.e(TAG, "resolveIntent: " + e.getMessage());
|
||||||
Toaster.showLong("NFC数据读取失败,请检查配置是否正确");
|
Toaster.showLong("NFC数据读取失败,请检查配置是否正确");
|
||||||
@@ -168,17 +168,17 @@ public class NfcActivity extends AppCompatActivity {
|
|||||||
public class BtnClick {
|
public class BtnClick {
|
||||||
public void write(View view) {
|
public void write(View view) {
|
||||||
String name = mBinding.etName.getText().toString();
|
String name = mBinding.etName.getText().toString();
|
||||||
String phone = mBinding.etPhone.getText().toString();
|
String phoneNumber = mBinding.etPhone.getText().toString();
|
||||||
if (TextUtils.isEmpty(name)) {
|
if (TextUtils.isEmpty(name)) {
|
||||||
Toaster.show("请输入联系人姓名");
|
Toaster.show("请输入联系人姓名");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (TextUtils.isEmpty(phone)) {
|
if (TextUtils.isEmpty(phoneNumber)) {
|
||||||
Toaster.show("请输入联系人手机号");
|
Toaster.show("请输入联系人手机号");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
WechatContact wechatContact = new WechatContact(name, phone);
|
ContactInfo contactInfo = new ContactInfo(name, phoneNumber);
|
||||||
writeTag(GsonUtils.toJSONString(wechatContact), mTag);
|
writeTag(GsonUtils.toJSONString(contactInfo), mTag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
package com.ttstd.elderlyassistant.activity.scheme;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import com.google.android.accessibility.selecttospeak.SelectToSpeakService;
|
||||||
|
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.bean.ContactInfo;
|
||||||
|
import com.ttstd.elderlyassistant.utils.AESUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
public class SchemeActivity extends AppCompatActivity {
|
||||||
|
private static final String TAG = "SchemeActivity";
|
||||||
|
|
||||||
|
private static final int REQUEST_CALL_PHONE = 1;
|
||||||
|
private static final int REQUEST_SEND_SMS = 2;
|
||||||
|
|
||||||
|
private Gson mGson = new Gson();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_scheme);
|
||||||
|
Intent intent = getIntent();
|
||||||
|
parseIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
parseIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理权限请求结果
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
if (requestCode == REQUEST_CALL_PHONE) {
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// 用户授权后重新获取号码并拨号
|
||||||
|
directCall(mTempPhoneNumber);
|
||||||
|
} else {
|
||||||
|
// 权限被拒绝,提示用户
|
||||||
|
Toast.makeText(this, "权限被拒绝,无法拨号", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else if (requestCode == REQUEST_SEND_SMS) {
|
||||||
|
// if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// sendSms(mTempPhoneNumber); // 用户授权后发送短信
|
||||||
|
// } else {
|
||||||
|
// Toast.makeText(this, "短信发送权限被拒绝", Toast.LENGTH_SHORT).show();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void parseIntent(Intent intent) {
|
||||||
|
if (intent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.e(TAG, "parseIntent: " + intent);
|
||||||
|
Uri uri = getIntent().getData();
|
||||||
|
if (uri != null) {
|
||||||
|
String path = uri.getPath();
|
||||||
|
Log.e(TAG, "parseIntent: path = " + path);
|
||||||
|
if (TextUtils.isEmpty(path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String detail = uri.getQueryParameter("detail");
|
||||||
|
if (TextUtils.isEmpty(detail)) {
|
||||||
|
Toaster.show("没有获取到联系人数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Type listType = new TypeToken<ContactInfo>() {
|
||||||
|
}.getType();
|
||||||
|
switch (path) {
|
||||||
|
case "/contactInfo":
|
||||||
|
try {
|
||||||
|
ContactInfo contactInfo = mGson.fromJson(detail, listType);
|
||||||
|
parseContactInfo(contactInfo);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "parseIntent: " + e.getMessage());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "/encrypted":
|
||||||
|
try {
|
||||||
|
String decryptedString = AESUtil.decrypt(detail.trim());
|
||||||
|
ContactInfo contactInfo = mGson.fromJson(decryptedString, listType);
|
||||||
|
parseContactInfo(contactInfo);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "parseEncryptedData: " + e.getMessage());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String mTempPhoneNumber;
|
||||||
|
|
||||||
|
private void parseContactInfo(ContactInfo contactInfo) {
|
||||||
|
if (contactInfo != null) {
|
||||||
|
int operation = contactInfo.getOperation();
|
||||||
|
String phoneNumber = contactInfo.getPhone();
|
||||||
|
switch (operation) {
|
||||||
|
case 1:
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// 未授权时请求权限
|
||||||
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALL_PHONE);
|
||||||
|
mTempPhoneNumber = phoneNumber;
|
||||||
|
} else {
|
||||||
|
// 已授权则直接拨号
|
||||||
|
directCall(phoneNumber);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// if (ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// // 动态请求权限
|
||||||
|
// ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.SEND_SMS}, REQUEST_SEND_SMS);
|
||||||
|
// mTempPhoneNumber = phoneNumber;
|
||||||
|
// } else {
|
||||||
|
sendSms(phoneNumber);
|
||||||
|
// }
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
Intent accessibilityIntent = new Intent(SchemeActivity.this, SelectToSpeakService.class);
|
||||||
|
accessibilityIntent.putExtra("WechatInfo", contactInfo);
|
||||||
|
startService(accessibilityIntent);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void directCall(String phoneNumber) {
|
||||||
|
if (TextUtils.isEmpty(phoneNumber)) {
|
||||||
|
Toaster.show("未获取到手机号码");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Intent dialerIntent = new Intent(Intent.ACTION_CALL);
|
||||||
|
Uri uri = Uri.parse("tel:" + phoneNumber);
|
||||||
|
dialerIntent.setData(uri);
|
||||||
|
startActivity(dialerIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendSms(String phoneNumber) {
|
||||||
|
if (TextUtils.isEmpty(phoneNumber)) {
|
||||||
|
Toaster.show("未获取到手机号码");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse("sms:" + phoneNumber));
|
||||||
|
intent.putExtra("sms_body", "");
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.ttstd.elderlyassistant.activity.write;
|
|||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.nfc.NdefMessage;
|
import android.nfc.NdefMessage;
|
||||||
import android.nfc.NdefRecord;
|
import android.nfc.NdefRecord;
|
||||||
import android.nfc.NfcAdapter;
|
import android.nfc.NfcAdapter;
|
||||||
@@ -17,13 +18,19 @@ import android.widget.Toast;
|
|||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.databinding.DataBindingUtil;
|
import androidx.databinding.DataBindingUtil;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.hjq.toast.Toaster;
|
import com.hjq.toast.Toaster;
|
||||||
import com.ttstd.elderlyassistant.R;
|
import com.ttstd.elderlyassistant.R;
|
||||||
|
import com.ttstd.elderlyassistant.bean.ContactInfo;
|
||||||
import com.ttstd.elderlyassistant.databinding.ActivityWriteMessageBinding;
|
import com.ttstd.elderlyassistant.databinding.ActivityWriteMessageBinding;
|
||||||
|
import com.ttstd.elderlyassistant.gson.GsonUtils;
|
||||||
|
import com.ttstd.elderlyassistant.utils.AESUtil;
|
||||||
import com.ttstd.elderlyassistant.utils.HexUtils;
|
import com.ttstd.elderlyassistant.utils.HexUtils;
|
||||||
import com.ttstd.elderlyassistant.utils.WriteMessageUtils;
|
import com.ttstd.elderlyassistant.utils.WriteMessageUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public class WriteMessageActivity extends AppCompatActivity {
|
public class WriteMessageActivity extends AppCompatActivity {
|
||||||
@@ -31,15 +38,19 @@ public class WriteMessageActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private ActivityWriteMessageBinding mBinding;
|
private ActivityWriteMessageBinding mBinding;
|
||||||
|
|
||||||
|
private Gson mGson = new Gson();
|
||||||
|
|
||||||
private NfcAdapter mNfcAdapter;
|
private NfcAdapter mNfcAdapter;
|
||||||
private PendingIntent mPendingIntent;
|
private PendingIntent mPendingIntent;
|
||||||
private Tag mTag;
|
private Tag mTag;
|
||||||
|
private int mOperationId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
// setContentView(R.layout.activity_write_message);
|
// setContentView(R.layout.activity_write_message);
|
||||||
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_write_message);
|
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_write_message);
|
||||||
|
mBinding.setLifecycleOwner(this);
|
||||||
mBinding.setClick(new BtnClick());
|
mBinding.setClick(new BtnClick());
|
||||||
|
|
||||||
// 获取NFC适配器实例
|
// 获取NFC适配器实例
|
||||||
@@ -124,10 +135,48 @@ public class WriteMessageActivity extends AppCompatActivity {
|
|||||||
// 提取第一条记录的文本
|
// 提取第一条记录的文本
|
||||||
NdefRecord record = ndefMessage.getRecords()[0];
|
NdefRecord record = ndefMessage.getRecords()[0];
|
||||||
String payload = new String(record.getPayload(), StandardCharsets.UTF_8);
|
String payload = new String(record.getPayload(), StandardCharsets.UTF_8);
|
||||||
Log.e("NFC", "NDEF Text: " + payload.substring(1)); // 跳过编码头
|
String text = payload.substring(1);
|
||||||
|
Log.e(TAG, "parseNdefData: NDEF Text: " + text); // 跳过编码头
|
||||||
|
|
||||||
|
Uri uri = Uri.parse(text);
|
||||||
|
String path = uri.getPath();
|
||||||
|
if (TextUtils.isEmpty(path)) {
|
||||||
|
Toaster.show("没有获取到联系人数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String detail = uri.getQueryParameter("detail");
|
||||||
|
if (TextUtils.isEmpty(detail)) {
|
||||||
|
Toaster.show("没有获取到联系人数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Type listType = new TypeToken<ContactInfo>() {
|
||||||
|
}.getType();
|
||||||
|
|
||||||
|
switch (path) {
|
||||||
|
case "/contactInfo":
|
||||||
|
mBinding.rbNoEncryption.setChecked(true);
|
||||||
|
try {
|
||||||
|
ContactInfo contactInfo = mGson.fromJson(detail, listType);
|
||||||
|
setContactInfo(contactInfo);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "parseNdefData: " + e.getMessage());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "/encrypted":
|
||||||
|
mBinding.rbEncryption.setChecked(true);
|
||||||
|
try {
|
||||||
|
String trimString = detail.trim();
|
||||||
|
String decryptedString = AESUtil.decrypt(trimString);
|
||||||
|
ContactInfo contactInfo = mGson.fromJson(decryptedString, listType);
|
||||||
|
setContactInfo(contactInfo);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "parseEncryptedData: " + e.getMessage());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e("NFC", "NDEF解析失败" + e.getMessage());
|
Log.e(TAG, "NDEF解析失败" + e.getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
ndef.close();
|
ndef.close();
|
||||||
@@ -138,21 +187,70 @@ public class WriteMessageActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setContactInfo(ContactInfo contactInfo) {
|
||||||
|
mBinding.setContactInfo(contactInfo);
|
||||||
|
}
|
||||||
|
|
||||||
public class BtnClick {
|
public class BtnClick {
|
||||||
public void write(View view) {
|
public void write(View view) {
|
||||||
if (mTag == null) {
|
if (mTag == null) {
|
||||||
Toaster.showLong("NFC卡片未连接");
|
Toaster.showLong("NFC卡片未连接");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Editable editable = mBinding.etPhone.getText();
|
Editable nameEditable = mBinding.etName.getText();
|
||||||
if (TextUtils.isEmpty(editable)) {
|
Editable phoneEditable = mBinding.etPhone.getText();
|
||||||
Toaster.showLong("请输入电话");
|
Editable wxidEditable = mBinding.etWxid.getText();
|
||||||
|
Editable qqEditable = mBinding.etQq.getText();
|
||||||
|
Editable tagEditable = mBinding.etTag.getText();
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(nameEditable)) {
|
||||||
|
Toaster.showLong("请输入姓名或昵称");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String phone = editable.toString();
|
String name = nameEditable.toString();
|
||||||
if (!phone.startsWith("+86"))
|
String phoneNumber = phoneEditable.toString();
|
||||||
phone = "+86" + phone;
|
String wechatId = wxidEditable.toString();
|
||||||
WriteMessageUtils.writePhoneToTag(mTag, phone);
|
String qq = qqEditable.toString();
|
||||||
|
String tag = tagEditable.toString();
|
||||||
|
|
||||||
|
int checkedId = mBinding.rgOperation.getCheckedRadioButtonId();
|
||||||
|
if (checkedId != -1) {
|
||||||
|
View checkedView = mBinding.rgOperation.findViewById(checkedId);
|
||||||
|
int index = mBinding.rgOperation.indexOfChild(checkedView);
|
||||||
|
Log.d("SelectedIndex", "位置: " + index); // 输出索引(从0开始)
|
||||||
|
mOperationId = index + 1;
|
||||||
|
} else {
|
||||||
|
Log.d("SelectedIndex", "未选中任何项");
|
||||||
|
mOperationId = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactInfo contactInfo = new ContactInfo();
|
||||||
|
contactInfo.setName(name);
|
||||||
|
contactInfo.setPhone(phoneNumber);
|
||||||
|
contactInfo.setWxid(wechatId);
|
||||||
|
contactInfo.setQq(qq);
|
||||||
|
contactInfo.setTag(tag);
|
||||||
|
contactInfo.setOperation(mOperationId);
|
||||||
|
|
||||||
|
if (mBinding.rbEncryption.isChecked()) {
|
||||||
|
try {
|
||||||
|
String jsonString = GsonUtils.toJSONString(contactInfo);
|
||||||
|
String encryptedString = AESUtil.encrypt(jsonString.trim());
|
||||||
|
WriteMessageUtils.writeUrlAddPackage(mTag, "elderlyassistant://ttstd.com/encrypted?detail=" + encryptedString, "com.ttstd.elderlyassistant");
|
||||||
|
Toaster.show("写入联系人成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Toaster.show("写入联系人失败");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
WriteMessageUtils.writeUrlAddPackage(mTag, "elderlyassistant://ttstd.com/contactInfo?detail=" + GsonUtils.toJSONString(contactInfo), "com.ttstd.elderlyassistant");
|
||||||
|
Toaster.show("写入联系人成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Toaster.show("写入联系人失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeQqMusic(View view) {
|
public void writeQqMusic(View view) {
|
||||||
@@ -160,15 +258,29 @@ public class WriteMessageActivity extends AppCompatActivity {
|
|||||||
Toaster.showLong("NFC卡片未连接");
|
Toaster.showLong("NFC卡片未连接");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
WriteMessageUtils.writeUrlToTag(mTag, "qqmusic://qq.com/media/playSonglist?p={\"song\":[{\"songmid\":\"003Hr2m138zmPR\"}]}");
|
try {
|
||||||
|
WriteMessageUtils.writeUrlToTag(mTag, "qqmusic://qq.com/media/playSonglist?p={\"song\":[{\"songmid\":\"003Hr2m138zmPR\"}]}");
|
||||||
|
Toaster.show("写入联系人成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Toaster.show("写入联系人失败");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeQqMusicForIos(View view) {
|
public void writeUrlAddPackage(View view) {
|
||||||
if (mTag == null) {
|
if (mTag == null) {
|
||||||
Toaster.showLong("NFC卡片未连接");
|
Toaster.showLong("NFC卡片未连接");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
WriteMessageUtils.writeUrlToTagIos(mTag, "qqmusic://qq.com/media/playSonglist?p={\"song\":[{\"songmid\":\"003aAPj81VWrbL\"}]}","com.tencent.qqmusic");
|
try {
|
||||||
|
WriteMessageUtils.writeUrlAddPackage(mTag,
|
||||||
|
"elderlyassistant://ttstd.com/contactInfo?detail={ \"name\": \"教父\", \"phone\": \"18108347897\", \"tag\": \"家人\", \"wxid\": \"wx_godfather\", \"operation\": 1, \"version\": 1 }",
|
||||||
|
"com.ttstd.elderlyassistant");
|
||||||
|
Toaster.show("写入联系人成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Toaster.show("写入联系人失败");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.ttstd.elderlyassistant.activity.write;
|
||||||
|
|
||||||
|
import androidx.databinding.ObservableInt;
|
||||||
|
import androidx.lifecycle.ViewModel;
|
||||||
|
|
||||||
|
import com.ttstd.elderlyassistant.R;
|
||||||
|
|
||||||
|
public class WriteMessageViewModel extends ViewModel {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,24 +7,26 @@ import com.google.gson.JsonParser;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
public class WechatContact implements Serializable {
|
public class ContactInfo implements Serializable {
|
||||||
private static final long serialVersionUID = -5570426400839799887L;
|
private static final long serialVersionUID = -5570426400839799887L;
|
||||||
|
|
||||||
public String name;
|
public String name;
|
||||||
public String phone;
|
public String phone;
|
||||||
public String tag;
|
public String tag;
|
||||||
|
public String wxid;
|
||||||
|
public String qq;
|
||||||
/**
|
/**
|
||||||
* 0 电话 1 短信 2 微信语音 3 微信视频
|
* 1 电话 2 短信 3 微信语音 4 微信视频 5 QQ语音 6 QQ视频
|
||||||
*/
|
*/
|
||||||
int operation;
|
int operation;
|
||||||
int version;
|
int version;
|
||||||
|
|
||||||
|
|
||||||
public WechatContact() {
|
public ContactInfo() {
|
||||||
|
this.version = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WechatContact(String name, String phone) {
|
public ContactInfo(String name, String phone) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.phone = phone;
|
this.phone = phone;
|
||||||
this.operation = 3;
|
this.operation = 3;
|
||||||
@@ -47,7 +49,6 @@ public class WechatContact implements Serializable {
|
|||||||
this.phone = phone;
|
this.phone = phone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getTag() {
|
public String getTag() {
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
@@ -56,6 +57,22 @@ public class WechatContact implements Serializable {
|
|||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getWxid() {
|
||||||
|
return wxid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWxid(String wxid) {
|
||||||
|
this.wxid = wxid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQq() {
|
||||||
|
return qq;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQq(String qq) {
|
||||||
|
this.qq = qq;
|
||||||
|
}
|
||||||
|
|
||||||
public int getOperation() {
|
public int getOperation() {
|
||||||
return operation;
|
return operation;
|
||||||
}
|
}
|
||||||
@@ -64,6 +81,14 @@ public class WechatContact implements Serializable {
|
|||||||
this.operation = operation;
|
this.operation = operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(int version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.ttstd.elderlyassistant.utils;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class AESUtil {
|
||||||
|
private static final String ALGORITHM = "AES/CBC/PKCS5Padding"; // 使用 CBC 模式和 PKCS5 填充
|
||||||
|
private static final String SECRET_KEY = "YourSecretKey123"; // 密钥(16/24/32 字节)
|
||||||
|
private static final String IV = "RandomInitVector"; // 初始化向量(16 字节)
|
||||||
|
|
||||||
|
// 加密
|
||||||
|
public static String encrypt(String input) throws Exception {
|
||||||
|
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
||||||
|
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), "AES");
|
||||||
|
IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
|
||||||
|
byte[] encryptedBytes = cipher.doFinal(input.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return Base64.getEncoder().encodeToString(encryptedBytes); // Base64 编码输出
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
public static String decrypt(String encryptedInput) throws Exception {
|
||||||
|
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
||||||
|
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), "AES");
|
||||||
|
IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
|
||||||
|
byte[] decodedBytes = Base64.getDecoder().decode(encryptedInput);
|
||||||
|
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
|
||||||
|
return new String(decryptedBytes, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,60 +15,45 @@ import java.io.IOException;
|
|||||||
public class WriteMessageUtils {
|
public class WriteMessageUtils {
|
||||||
private static final String TAG = "WriteMessageUtils";
|
private static final String TAG = "WriteMessageUtils";
|
||||||
|
|
||||||
public static void writePhoneToTag(Tag tag, String phoneNumber) {
|
public static void writePhoneToTag(Tag tag, String phoneNumber) throws Exception {
|
||||||
// 构建tel:协议的URI
|
// 构建tel:协议的URI
|
||||||
Uri uri = Uri.parse("tel:" + phoneNumber);
|
Uri uri = Uri.parse("tel:" + phoneNumber);
|
||||||
// 创建URI类型的NdefRecord
|
// 创建URI类型的NdefRecord
|
||||||
NdefRecord uriRecord = NdefRecord.createUri(uri);
|
NdefRecord uriRecord = NdefRecord.createUri(uri);
|
||||||
NdefMessage message = new NdefMessage(new NdefRecord[]{uriRecord});
|
NdefMessage message = new NdefMessage(new NdefRecord[]{uriRecord});
|
||||||
|
|
||||||
try {
|
Ndef ndef = Ndef.get(tag);
|
||||||
Ndef ndef = Ndef.get(tag);
|
ndef.connect();
|
||||||
ndef.connect();
|
ndef.writeNdefMessage(message); // 写入NDEF消息
|
||||||
ndef.writeNdefMessage(message); // 写入NDEF消息
|
ndef.close();
|
||||||
ndef.close();
|
|
||||||
Toaster.show("写入联系人成功");
|
|
||||||
} catch (IOException | FormatException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeUrlToTag(Tag tag, String url) {
|
public static void writeUrlToTag(Tag tag, String url) throws Exception {
|
||||||
// 确保URL包含协议(如http://)
|
// 确保URL包含协议(如http://)
|
||||||
Uri uri = Uri.parse(url);
|
Uri uri = Uri.parse(url);
|
||||||
NdefRecord uriRecord = NdefRecord.createUri(uri);
|
NdefRecord uriRecord = NdefRecord.createUri(uri);
|
||||||
NdefMessage message = new NdefMessage(new NdefRecord[]{uriRecord});
|
NdefMessage message = new NdefMessage(new NdefRecord[]{uriRecord});
|
||||||
|
|
||||||
try {
|
Ndef ndef = Ndef.get(tag);
|
||||||
Ndef ndef = Ndef.get(tag);
|
ndef.connect();
|
||||||
ndef.connect();
|
ndef.writeNdefMessage(message);
|
||||||
ndef.writeNdefMessage(message);
|
ndef.close();
|
||||||
ndef.close();
|
|
||||||
Toaster.show("写入Url成功");
|
|
||||||
} catch (IOException | FormatException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeUrlToTagIos(Tag tag, String url, String pkg) {
|
public static void writeUrlAddPackage(Tag tag, String url, String pkg) throws Exception {
|
||||||
|
Log.e(TAG, "writeUrlAddPackage: url = " + url);
|
||||||
|
String fixedUrl = url.replace("+", "%2B"); // 替换 + 为 %2B
|
||||||
// 1. 创建HTTP Record(HTTPS链接)
|
// 1. 创建HTTP Record(HTTPS链接)
|
||||||
Uri uri = Uri.parse(url);
|
Uri uri = Uri.parse(fixedUrl);
|
||||||
NdefRecord httpRecord = NdefRecord.createUri(uri);
|
NdefRecord httpRecord = NdefRecord.createUri(uri);
|
||||||
|
// 2. 创建Android应用包名Record
|
||||||
// 2. 创建Android应用包名Record
|
|
||||||
NdefRecord androidRecord = NdefRecord.createApplicationRecord(pkg);
|
NdefRecord androidRecord = NdefRecord.createApplicationRecord(pkg);
|
||||||
|
// 3. 按顺序组合记录
|
||||||
// 3. 按顺序组合记录
|
|
||||||
NdefMessage message = new NdefMessage(new NdefRecord[]{httpRecord, androidRecord});
|
NdefMessage message = new NdefMessage(new NdefRecord[]{httpRecord, androidRecord});
|
||||||
|
// 4. 写入NFC标签
|
||||||
// 4. 写入NFC标签
|
Ndef ndef = Ndef.get(tag); // 获取标签对象
|
||||||
try {
|
ndef.connect();
|
||||||
Ndef ndef = Ndef.get(tag); // 获取标签对象
|
ndef.writeNdefMessage(message);
|
||||||
ndef.connect();
|
|
||||||
ndef.writeNdefMessage(message);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, "writeUrlToTagIos: " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
type="com.ttstd.elderlyassistant.activity.nfc.NfcActivity.BtnClick" />
|
type="com.ttstd.elderlyassistant.activity.nfc.NfcActivity.BtnClick" />
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="wechatContact"
|
name="contactInfo"
|
||||||
type="com.ttstd.elderlyassistant.bean.WechatContact" />
|
type="com.ttstd.elderlyassistant.bean.ContactInfo" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
@@ -24,10 +24,10 @@
|
|||||||
android:id="@+id/et_name"
|
android:id="@+id/et_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ems="10"
|
android:maxLength="15"
|
||||||
android:hint="Name"
|
android:hint="Name"
|
||||||
android:inputType="text"
|
android:inputType="text"
|
||||||
android:text="@{wechatContact.name}"
|
android:text="@{contactInfo.name}"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
@@ -38,12 +38,12 @@
|
|||||||
android:id="@+id/et_phone"
|
android:id="@+id/et_phone"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ems="10"
|
android:maxLength="15"
|
||||||
android:hint="phone"
|
android:hint="phoneNumber"
|
||||||
android:inputType="phone"
|
android:inputType="phone"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@{wechatContact.phone}"
|
android:text="@{contactInfo.phone}"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/et_name" />
|
app:layout_constraintTop_toBottomOf="@id/et_name" />
|
||||||
|
|||||||
9
app/src/main/res/layout/activity_scheme.xml
Normal file
9
app/src/main/res/layout/activity_scheme.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout 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.scheme.SchemeActivity">
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -9,67 +9,235 @@
|
|||||||
<variable
|
<variable
|
||||||
name="click"
|
name="click"
|
||||||
type="com.ttstd.elderlyassistant.activity.write.WriteMessageActivity.BtnClick" />
|
type="com.ttstd.elderlyassistant.activity.write.WriteMessageActivity.BtnClick" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="contactInfo"
|
||||||
|
type="com.ttstd.elderlyassistant.bean.ContactInfo" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="viewModel"
|
||||||
|
type="com.ttstd.elderlyassistant.activity.write.WriteMessageViewModel" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.core.widget.NestedScrollView
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
<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_width="match_parent"
|
||||||
android:layout_height="wrap_content"/>
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/et_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="姓名、微信昵称(必填)"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@{contactInfo.name}"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/et_phone"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="手机号码(选填)"
|
||||||
|
android:inputType="phone"
|
||||||
|
android:maxLength="15"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@{contactInfo.phone}"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/et_wxid"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="微信ID或QQ号码(选填)"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@{contactInfo.wxid}"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/et_qq"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="QQ号码"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@{contactInfo.qq}"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/et_tag"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="微信通讯录分组标签(选填)"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@{contactInfo.tag}"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="自动操作类型:"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
android:id="@+id/rg_operation"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checkedButton="@id/rb_dialere"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_dialere"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:checked="@{contactInfo.operation==1}"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="拨打电话号码" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_sms"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:checked="@{contactInfo.operation==2}"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="发送短信" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_wechat_voice"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:checked="@{contactInfo.operation==3}"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="拨打微信电话" />
|
||||||
|
|
||||||
|
|
||||||
<Button
|
<RadioButton
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/rb_wechat_video"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:onClick="@{click::writeQqMusic}"
|
android:layout_height="40dp"
|
||||||
android:text="QQ音乐"
|
android:checked="@{contactInfo.operation==4}"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:maxLines="1"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:singleLine="true"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/et_phone" />
|
android:text="拨打微信视频" />
|
||||||
|
|
||||||
<Button
|
<RadioButton
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/rb_qq_voice"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:onClick="@{click::writeQqMusicForIos}"
|
android:layout_height="40dp"
|
||||||
android:text="QQ音乐(兼容iPhone)"
|
android:checked="@{contactInfo.operation==5}"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:maxLines="1"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:singleLine="true"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/et_phone" />
|
android:text="拨打QQ电话" />
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_qq_video"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:checked="@{contactInfo.operation==6}"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="拨打QQ视频" />
|
||||||
|
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="是否进行联系人加密:"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
android:id="@+id/rg_encrypt"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checkedButton="@id/rb_no_encryption"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_no_encryption"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="不加密(若要分享给他人请选择不加密)" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_encryption"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="加密(若担心个人信息泄露请选择加密,或只填写昵称)" />
|
||||||
|
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_id"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:onClick="@{click::write}"
|
||||||
|
android:text="写入"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/et_phone" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:onClick="@{click::writeQqMusic}"
|
||||||
|
android:text="QQ音乐"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/et_phone" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:onClick="@{click::writeUrlAddPackage}"
|
||||||
|
android:text="Scheme+app"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/et_phone" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</layout>
|
</layout>
|
||||||
Reference in New Issue
Block a user