优化通过scheme直接拨号和视频,增加数据加密。优化ContactInfo数据结构。优化无障碍流程
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.ttstd.elderlyassistant">
|
||||
|
||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||
<uses-permission android:name="android.permission.SEND_SMS" />
|
||||
<!-- NFC权限 -->
|
||||
<uses-permission android:name="android.permission.NFC" /> <!-- 声明只有带NFC的手机才可以下载 -->
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.nfc"
|
||||
android:required="true" />
|
||||
@@ -53,16 +57,31 @@
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- <activity-->
|
||||
<!-- android:name=".activity.third_party.NfcQQMusicActivity"-->
|
||||
<!-- android:launchMode="singleTask">-->
|
||||
<!-- <intent-filter>-->
|
||||
<!-- <action android:name="android.nfc.action.NDEF_DISCOVERED" />-->
|
||||
<!-- <category android:name="android.intent.category.DEFAULT" />-->
|
||||
<!-- <!– 匹配 QQ 音乐 Scheme –>-->
|
||||
<!-- <data android:scheme="qqmusic" />-->
|
||||
<!-- </intent-filter>-->
|
||||
<!-- </activity>-->
|
||||
|
||||
<!-- <activity -->
|
||||
<!-- android:name=".activity.third_party.NfcQQMusicActivity" -->
|
||||
<!-- android:launchMode="singleTask"> -->
|
||||
<!-- <intent-filter> -->
|
||||
<!-- <action android:name="android.nfc.action.NDEF_DISCOVERED" /> -->
|
||||
<!-- <category android:name="android.intent.category.DEFAULT" /> -->
|
||||
<!-- <!– 匹配 QQ 音乐 Scheme –> -->
|
||||
<!-- <data android:scheme="qqmusic" /> -->
|
||||
<!-- </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
|
||||
android:name="com.google.android.accessibility.selecttospeak.SelectToSpeakService"
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.widget.Toast;
|
||||
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.elderlyassistant.bean.WechatContact;
|
||||
import com.ttstd.elderlyassistant.bean.ContactInfo;
|
||||
import com.ttstd.elderlyassistant.config.CommonConfig;
|
||||
|
||||
import java.util.List;
|
||||
@@ -39,7 +39,7 @@ import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.functions.Consumer;
|
||||
|
||||
/**
|
||||
* 通过微信标签最高支持8.0.49,8.0.50 获取不到数据
|
||||
* 通过微信标签最高支持8.0.49
|
||||
* 8.0.54 可以获取
|
||||
* 通过 {@link AccessibilityService#getWindows}和修改accessibility-service 配置能遍历屏幕元素
|
||||
*/
|
||||
@@ -56,8 +56,8 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
private static final String TAG_TEXT = "标签";
|
||||
private static final String PARENT_VIDEO_TEXT = "视频通话";
|
||||
|
||||
private static final String VOICE_TEXT = "语音通话";
|
||||
private static final String VIDEO_TEXT = "视频通话";
|
||||
private static final String CALL_TEXT = "语音通话";
|
||||
|
||||
private static final String RECEIVE_DESCRIPTION = "接听";
|
||||
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_CLOSE_TEXT = "免提,已关闭";
|
||||
|
||||
public static final int TYPE_VOICE = 2;
|
||||
public static final int TYPE_VIDEO = 3;
|
||||
public static final int TYPE_VOICE = 3;
|
||||
public static final int TYPE_VIDEO = 4;
|
||||
|
||||
private static final int WAIT_TIME = 1500;
|
||||
|
||||
private WechatContact mContact;
|
||||
private String mName = "";//微信昵称
|
||||
|
||||
private ContactInfo mContact;
|
||||
/**
|
||||
* 微信昵称
|
||||
*/
|
||||
private String mName = "";
|
||||
private int mCallType = TYPE_VOICE;
|
||||
|
||||
private Step mCurrentStep = Step.WAITING;
|
||||
private Procedure mCurrentProcedure = Procedure.WAITING;
|
||||
private boolean mAutoAccept = false;
|
||||
|
||||
private Disposable mDisposable;
|
||||
@@ -92,11 +94,11 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
Log.e(TAG, "onCreate: ");
|
||||
registerSettingReceiver();
|
||||
mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_CALL_AUTO_ACCEPT_KEY, false);
|
||||
dealingAccessibilityEvent();
|
||||
|
||||
initDisposable();
|
||||
}
|
||||
|
||||
private void dealingAccessibilityEvent() {
|
||||
private void initDisposable() {
|
||||
mDisposable = Observable.create(new ObservableOnSubscribe<AccessibilityEvent>() {
|
||||
@Override
|
||||
public void subscribe(@NonNull ObservableEmitter<AccessibilityEvent> emitter) throws Throwable {
|
||||
@@ -106,23 +108,23 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
.subscribe(new Consumer<AccessibilityEvent>() {
|
||||
@Override
|
||||
public void accept(AccessibilityEvent accessibilityEvent) throws Throwable {
|
||||
Log.e(TAG, "accept: ");
|
||||
_onAccessibilityEvent(accessibilityEvent);
|
||||
Log.e(TAG, "initDisposable accept: ");
|
||||
parseAccessibilityEvent(accessibilityEvent);
|
||||
}
|
||||
});
|
||||
Log.e(TAG, "dealingAccessibilityEvent: mDisposable hash = " + mDisposable.hashCode());
|
||||
Log.e(TAG, "initDisposable: mDisposable hash = " + mDisposable.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.e(TAG, "onStartCommand: ");
|
||||
if (intent != null) {
|
||||
mContact = (WechatContact) intent.getSerializableExtra("WechatInfo");
|
||||
mContact = (ContactInfo) intent.getSerializableExtra("WechatInfo");
|
||||
Log.e(TAG, "onStartCommand: wechatInfo = " + mContact);
|
||||
if (mContact != null) {
|
||||
mName = mContact.getName();
|
||||
mCallType = mContact.getOperation();
|
||||
mCurrentStep = Step.CLICK_HOME;
|
||||
mCurrentProcedure = Procedure.CLICK_HOME;
|
||||
launchWeChat();
|
||||
} else {
|
||||
Toaster.showLong("没有获取到联系人数据");
|
||||
@@ -155,12 +157,12 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
}
|
||||
|
||||
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) {
|
||||
String currentPackageName = event.getPackageName().toString();
|
||||
String currentClassName = event.getClassName().toString();
|
||||
|
||||
switch (mCurrentStep) {
|
||||
switch (mCurrentProcedure) {
|
||||
case WAITING:
|
||||
if (!TextUtils.isEmpty(currentPackageName) && "com.android.incallui".equals(currentPackageName)) {
|
||||
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.LoginPasswordUI":
|
||||
Toaster.showLong("请先登录微信");
|
||||
mCurrentStep = Step.WAITING;
|
||||
mCurrentProcedure = Procedure.WAITING;
|
||||
break;
|
||||
case "com.tencent.mm.plugin.label.ui.ContactLabelManagerUI":
|
||||
|
||||
@@ -197,20 +199,20 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
private void _onAccessibilityEvent(AccessibilityEvent event) {
|
||||
Log.e(TAG, "_onAccessibilityEvent: " + mCurrentStep);
|
||||
switch (mCurrentStep) {
|
||||
private void parseAccessibilityEvent(AccessibilityEvent event) {
|
||||
Log.e(TAG, "parseAccessibilityEvent: " + mCurrentProcedure);
|
||||
switch (mCurrentProcedure) {
|
||||
case WAITING:
|
||||
mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_CALL_AUTO_ACCEPT_KEY, false);
|
||||
Log.e(TAG, "_onAccessibilityEvent: mAutoAccept = " + mAutoAccept);
|
||||
Log.e(TAG, "parseAccessibilityEvent: mAutoAccept = " + mAutoAccept);
|
||||
if (!mAutoAccept) {
|
||||
return;
|
||||
}
|
||||
if (stepAnswer(Property.DESCRIPTION, RECEIVE_DESCRIPTION)) {
|
||||
mCurrentStep = Step.WECHAT_HANDS_FREE;
|
||||
mCurrentProcedure = Procedure.WECHAT_HANDS_FREE;
|
||||
Toast.makeText(this, "已自动接听视频/语音", Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
mCurrentStep = Step.WAITING;
|
||||
mCurrentProcedure = Procedure.WAITING;
|
||||
// clickAnswer();
|
||||
}
|
||||
break;
|
||||
@@ -221,29 +223,29 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
if (findHandsFree(Property.DESCRIPTION, DIALER_HANDS_FREE_CLOSE_TEXT)) {
|
||||
dialerHandsFree(Property.TEXT, DIALER_HANDS_FREE_TEXT);
|
||||
} else {
|
||||
mCurrentStep = Step.WAITING;
|
||||
mCurrentProcedure = Procedure.WAITING;
|
||||
}
|
||||
break;
|
||||
case CLICK_HOME://主页能找到直接点击进去更多
|
||||
if (stepHome(Property.TEXT, mName)) {
|
||||
Log.e(TAG, "_onAccessibilityEvent: not found contact in home");
|
||||
Log.e(TAG, "parseAccessibilityEvent: not found contact in home");
|
||||
} 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);
|
||||
}
|
||||
break;
|
||||
case CLICK_SEARCH:
|
||||
putString(mName, Step.CLICK_SEARCH_CONTACT);
|
||||
putString(mName, Procedure.CLICK_SEARCH_CONTACT);
|
||||
break;
|
||||
case CLICK_SEARCH_CONTACT:
|
||||
if (findSearchContact(Step.FIND_CONTACT)) {
|
||||
findSearchContact(Property.TEXT, mName, Step.CLICK_QUICK_WECHAT_CALL);
|
||||
if (findSearchContact(Procedure.FIND_CONTACT)) {
|
||||
findSearchContact(Property.TEXT, mName, Procedure.CLICK_QUICK_WECHAT_CALL);
|
||||
} else {
|
||||
Toaster.show("没有找到联系人");
|
||||
}
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
case CLICK_TARGET://点击视频通话
|
||||
@@ -252,40 +254,34 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
break;
|
||||
|
||||
case CLICK_CONTACT://进入通讯录界面
|
||||
if (stepHome(Property.TEXT, CONTACT_TEXT, Step.FIND_TAG)) {
|
||||
Log.e(TAG, "_onAccessibilityEvent: enter contact");
|
||||
if (stepHome(Property.TEXT, CONTACT_TEXT, Procedure.FIND_TAG)) {
|
||||
Log.e(TAG, "parseAccessibilityEvent: enter contact");
|
||||
} else {
|
||||
touchContact();
|
||||
}
|
||||
break;
|
||||
case FIND_CONTACT://模拟滑动找到联系人
|
||||
findSearchContact(Property.TEXT, mName, Step.CLICK_QUICK_WECHAT_CALL);
|
||||
findSearchContact(Property.TEXT, mName, Procedure.CLICK_QUICK_WECHAT_CALL);
|
||||
break;
|
||||
case FIND_TAG:
|
||||
step(Property.TEXT, TAG_TEXT, Step.CLICK_TAG);
|
||||
step(Property.TEXT, TAG_TEXT, Procedure.CLICK_TAG);
|
||||
break;
|
||||
case CLICK_TAG:
|
||||
|
||||
break;
|
||||
case CLICK_NAME://点击item
|
||||
findContact(Property.TEXT, mName, Step.CLICK_INFO);
|
||||
findContact(Property.TEXT, mName, Procedure.CLICK_INFO);
|
||||
break;
|
||||
case CLICK_INFO://进入个人信息页面
|
||||
stepCallDialog(Property.TEXT, DIALER_TEXT, Step.CLICK_CALL);
|
||||
stepCallDialog(Property.TEXT, DIALER_TEXT, Procedure.CLICK_CALL);
|
||||
break;
|
||||
case CLICK_CALL://打视频或者电话
|
||||
if (mCallType == TYPE_VIDEO) {
|
||||
step(Property.TEXT, VIDEO_TEXT, Step.WAITING);
|
||||
step(Property.TEXT, VIDEO_TEXT, Procedure.WAITING);
|
||||
} else if (mCallType == TYPE_VOICE) {
|
||||
step(Property.TEXT, CALL_TEXT, Step.WAITING);
|
||||
step(Property.TEXT, VOICE_TEXT, Procedure.WAITING);
|
||||
}
|
||||
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:
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
if (node != null) {
|
||||
Rect rect = new Rect();
|
||||
@@ -330,9 +326,9 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
return false;
|
||||
}
|
||||
clickNode(node);
|
||||
Log.e(TAG, "step: mCurrentStep: " + mCurrentStep + " done");
|
||||
mCurrentStep = nextStep;
|
||||
Log.e(TAG, "step: next: " + mCurrentStep);
|
||||
Log.e(TAG, "step: mCurrentStep: " + mCurrentProcedure + " done");
|
||||
mCurrentProcedure = nextProcedure;
|
||||
Log.e(TAG, "step: next: " + mCurrentProcedure);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -340,7 +336,7 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (node != null) {
|
||||
Rect rect = new Rect();
|
||||
@@ -350,9 +346,9 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
return false;
|
||||
}
|
||||
clickNode(node);
|
||||
Log.e(TAG, "step: mCurrentStep: " + mCurrentStep + " done");
|
||||
mCurrentStep = nextStep;
|
||||
Log.e(TAG, "step: next: " + mCurrentStep);
|
||||
Log.e(TAG, "step: mCurrentStep: " + mCurrentProcedure + " done");
|
||||
mCurrentProcedure = nextProcedure;
|
||||
Log.e(TAG, "step: next: " + mCurrentProcedure);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -363,35 +359,35 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
private void touchContact() {
|
||||
boolean successful = clickByPoint(268, 1440);
|
||||
if (successful) {
|
||||
mCurrentStep = Step.FIND_TAG;
|
||||
mCurrentProcedure = Procedure.FIND_TAG;
|
||||
} else {
|
||||
mCurrentStep = Step.WAITING;
|
||||
mCurrentProcedure = Procedure.WAITING;
|
||||
Toaster.show("点击失败,请重试");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean findSearchContact(Step nextStep) {
|
||||
private boolean findSearchContact(Procedure nextProcedure) {
|
||||
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId("com.tencent.mm:id/gzf");
|
||||
Log.e(TAG, "findSearchContact: " + nodeInfos);
|
||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||
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");
|
||||
Log.e(TAG, "findSearchContact: " + nodeInfos);
|
||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||
if (optional.isPresent()) {
|
||||
AccessibilityNodeInfo nodeInfo = optional.get();
|
||||
clickNode(nodeInfo);
|
||||
mCurrentStep = nextStep;
|
||||
mCurrentProcedure = nextProcedure;
|
||||
} else {
|
||||
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");
|
||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||
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
|
||||
nodeInfo.performAction(ACTION_IME_ENTER_ID);
|
||||
}
|
||||
mCurrentStep = nextStep;
|
||||
mCurrentProcedure = nextProcedure;
|
||||
} else {
|
||||
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);
|
||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||
if (optional.isPresent()) {
|
||||
AccessibilityNodeInfo nodeInfo = optional.get();
|
||||
clickNode(nodeInfo);
|
||||
mCurrentStep = nextStep;
|
||||
mCurrentProcedure = nextProcedure;
|
||||
} else {
|
||||
Toaster.show("没有找到搜索按钮");
|
||||
}
|
||||
@@ -448,15 +444,15 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
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);
|
||||
if (node != null) {
|
||||
Log.e(TAG, "stepCallDialog: isVisibleToUser: " + node.isVisibleToUser());
|
||||
if (node.isVisibleToUser()) {
|
||||
clickNode(node);
|
||||
Log.e(TAG, "stepCallDialog: mCurrentStep: " + mCurrentStep + " done");
|
||||
mCurrentStep = nextStep;
|
||||
Log.e(TAG, "stepCallDialog: next: " + mCurrentStep);
|
||||
Log.e(TAG, "stepCallDialog: mCurrentStep: " + mCurrentProcedure + " done");
|
||||
mCurrentProcedure = nextProcedure;
|
||||
Log.e(TAG, "stepCallDialog: next: " + mCurrentProcedure);
|
||||
return true;
|
||||
} else {
|
||||
scrollDown();
|
||||
@@ -466,7 +462,7 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
if (mFindCount == mMaxCount) {
|
||||
Log.e("stepCallDialog", "mCurrentStep: max");
|
||||
Toast.makeText(this, "没有找到联系人", Toast.LENGTH_LONG).show();
|
||||
mCurrentStep = Step.WAITING;
|
||||
mCurrentProcedure = Procedure.WAITING;
|
||||
mFindCount = 0;
|
||||
return false;
|
||||
} else {
|
||||
@@ -483,12 +479,12 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
||||
if (node != null) {
|
||||
clickNode(node);
|
||||
Log.e(TAG, "stepHome: mCurrentStep: " + mCurrentStep + " done");
|
||||
mCurrentStep = Step.CLICK_QUICK_WECHAT_CALL;
|
||||
Log.e(TAG, "stepHome: next: " + mCurrentStep);
|
||||
Log.e(TAG, "stepHome: mCurrentStep: " + mCurrentProcedure + " done");
|
||||
mCurrentProcedure = Procedure.CLICK_QUICK_WECHAT_CALL;
|
||||
Log.e(TAG, "stepHome: next: " + mCurrentProcedure);
|
||||
return true;
|
||||
} else {
|
||||
mCurrentStep = Step.CLICK_SEARCH;
|
||||
mCurrentProcedure = Procedure.CLICK_SEARCH;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -497,20 +493,20 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
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);
|
||||
if (node != null) {
|
||||
clickNode(node);
|
||||
Log.e("findContact", "mCurrentStep: " + mCurrentStep + " done");
|
||||
mCurrentStep = nextStep;
|
||||
Log.e("findContact", "next: " + mCurrentStep);
|
||||
Log.e("findContact", "mCurrentStep: " + mCurrentProcedure + " done");
|
||||
mCurrentProcedure = nextProcedure;
|
||||
Log.e("findContact", "next: " + mCurrentProcedure);
|
||||
mFindCount = 0;
|
||||
return true;
|
||||
} else {
|
||||
if (mFindCount == mMaxCount) {
|
||||
Log.e("findContact", "mCurrentStep: max");
|
||||
Toast.makeText(this, "没有找到联系人", Toast.LENGTH_LONG).show();
|
||||
mCurrentStep = Step.WAITING;
|
||||
mCurrentProcedure = Procedure.WAITING;
|
||||
mFindCount = 0;
|
||||
return false;
|
||||
} else {
|
||||
@@ -620,9 +616,9 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
Log.e(TAG, "stepCall: " + point);
|
||||
clickByPoint(point.x, point.y);
|
||||
// clickNode(node);
|
||||
Log.e(TAG, "stepCall: mCurrentStep " + mCurrentStep + " done");
|
||||
mCurrentStep = Step.CLICK_CALL;
|
||||
Log.e(TAG, "stepCall: next " + mCurrentStep);
|
||||
Log.e(TAG, "stepCall: mCurrentStep " + mCurrentProcedure + " done");
|
||||
mCurrentProcedure = Procedure.CLICK_CALL;
|
||||
Log.e(TAG, "stepCall: next " + mCurrentProcedure);
|
||||
return true;
|
||||
} else {
|
||||
Log.e(TAG, "stepCall: not found");
|
||||
@@ -636,7 +632,7 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
if (accessibilityNodeInfo.isPresent()) {
|
||||
AccessibilityNodeInfo nodeInfo = accessibilityNodeInfo.get();
|
||||
clickNode(nodeInfo);
|
||||
mCurrentStep = Step.CLICK_CALL;
|
||||
mCurrentProcedure = Procedure.CLICK_CALL;
|
||||
} else {
|
||||
Toaster.show("没有找到通话按钮");
|
||||
}
|
||||
@@ -650,9 +646,9 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
clickByPoint(point.x, point.y - 50);
|
||||
clickByPoint(point.x, point.y);
|
||||
// clickNode(node);
|
||||
Log.e(TAG, "stepAnswer: mCurrentStep " + mCurrentStep + " done");
|
||||
mCurrentStep = Step.WAITING;
|
||||
Log.e(TAG, "stepAnswer: next " + mCurrentStep);
|
||||
Log.e(TAG, "stepAnswer: mCurrentStep " + mCurrentProcedure + " done");
|
||||
mCurrentProcedure = Procedure.WAITING;
|
||||
Log.e(TAG, "stepAnswer: next " + mCurrentProcedure);
|
||||
return true;
|
||||
} else {
|
||||
Log.e(TAG, "stepAnswer: not found");
|
||||
@@ -667,9 +663,9 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
node.getBoundsInScreen(rect);
|
||||
Log.e(TAG, "dialerHandsFree: rect = " + rect);
|
||||
clickNode(node);
|
||||
Log.e(TAG, "dialerHandsFree: mCurrentStep: " + mCurrentStep + " done");
|
||||
mCurrentStep = Step.WAITING;
|
||||
Log.e(TAG, "dialerHandsFree: next: " + mCurrentStep);
|
||||
Log.e(TAG, "dialerHandsFree: mCurrentStep: " + mCurrentProcedure + " done");
|
||||
mCurrentProcedure = Procedure.WAITING;
|
||||
Log.e(TAG, "dialerHandsFree: next: " + mCurrentProcedure);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -695,13 +691,13 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
clickByPoint(point.x, point.y - 50);
|
||||
clickByPoint(point.x, point.y);
|
||||
// clickNode(node);
|
||||
Log.e(TAG, "handsFree: mCurrentStep " + mCurrentStep + " done");
|
||||
mCurrentStep = Step.WAITING;
|
||||
Log.e(TAG, "handsFree: next " + mCurrentStep);
|
||||
Log.e(TAG, "handsFree: mCurrentStep " + mCurrentProcedure + " done");
|
||||
mCurrentProcedure = Procedure.WAITING;
|
||||
Log.e(TAG, "handsFree: next " + mCurrentProcedure);
|
||||
return true;
|
||||
} else {
|
||||
Log.e(TAG, "handsFree: not found");
|
||||
mCurrentStep = Step.WAITING;
|
||||
mCurrentProcedure = Procedure.WAITING;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -791,7 +787,7 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
return scrollScreen(0.5, 1.5);
|
||||
}
|
||||
|
||||
private enum Step {
|
||||
private enum Procedure {
|
||||
WAITING,
|
||||
//微信免提
|
||||
WECHAT_HANDS_FREE,
|
||||
@@ -822,7 +818,7 @@ public class SelectToSpeakService extends AccessibilityService {
|
||||
CLICK_CALL,
|
||||
CLICK_VIDEO_CALL;
|
||||
|
||||
private Step next() {
|
||||
private Procedure next() {
|
||||
return values()[(this.ordinal() + 1) % values().length];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ 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.bean.ContactInfo;
|
||||
import com.ttstd.elderlyassistant.databinding.ActivityMainBinding;
|
||||
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: contentSize = " + contentSize);
|
||||
Gson gson = new Gson();
|
||||
Type type = new TypeToken<WechatContact>() {
|
||||
Type type = new TypeToken<ContactInfo>() {
|
||||
}.getType();
|
||||
try {
|
||||
WechatContact contact = gson.fromJson(textRecord, type);
|
||||
ContactInfo contact = gson.fromJson(textRecord, type);
|
||||
Intent accessibilityIntent = new Intent(MainActivity.this, SelectToSpeakService.class);
|
||||
accessibilityIntent.putExtra("WechatInfo", contact);
|
||||
startService(accessibilityIntent);
|
||||
|
||||
@@ -22,7 +22,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.bean.WechatContact;
|
||||
import com.ttstd.elderlyassistant.bean.ContactInfo;
|
||||
import com.ttstd.elderlyassistant.databinding.ActivityNfcBinding;
|
||||
import com.ttstd.elderlyassistant.gson.GsonUtils;
|
||||
import com.ttstd.elderlyassistant.utils.NdefUtils;
|
||||
@@ -91,11 +91,11 @@ public class NfcActivity extends AppCompatActivity {
|
||||
if (!results.isEmpty()) {
|
||||
String jsonString = results.get(0);
|
||||
Gson gson = new Gson();
|
||||
Type type = new TypeToken<WechatContact>() {
|
||||
Type type = new TypeToken<ContactInfo>() {
|
||||
}.getType();
|
||||
try {
|
||||
WechatContact contact = gson.fromJson(jsonString, type);
|
||||
mBinding.setWechatContact(contact);
|
||||
ContactInfo contact = gson.fromJson(jsonString, type);
|
||||
mBinding.setContactInfo(contact);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "resolveIntent: " + e.getMessage());
|
||||
Toaster.showLong("NFC数据读取失败,请检查配置是否正确");
|
||||
@@ -168,17 +168,17 @@ public class NfcActivity extends AppCompatActivity {
|
||||
public class BtnClick {
|
||||
public void write(View view) {
|
||||
String name = mBinding.etName.getText().toString();
|
||||
String phone = mBinding.etPhone.getText().toString();
|
||||
String phoneNumber = mBinding.etPhone.getText().toString();
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
Toaster.show("请输入联系人姓名");
|
||||
return;
|
||||
}
|
||||
if (TextUtils.isEmpty(phone)) {
|
||||
if (TextUtils.isEmpty(phoneNumber)) {
|
||||
Toaster.show("请输入联系人手机号");
|
||||
return;
|
||||
}
|
||||
WechatContact wechatContact = new WechatContact(name, phone);
|
||||
writeTag(GsonUtils.toJSONString(wechatContact), mTag);
|
||||
ContactInfo contactInfo = new ContactInfo(name, phoneNumber);
|
||||
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.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.nfc.NdefMessage;
|
||||
import android.nfc.NdefRecord;
|
||||
import android.nfc.NfcAdapter;
|
||||
@@ -17,13 +18,19 @@ import android.widget.Toast;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
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.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.WriteMessageUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class WriteMessageActivity extends AppCompatActivity {
|
||||
@@ -31,15 +38,19 @@ public class WriteMessageActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityWriteMessageBinding mBinding;
|
||||
|
||||
private Gson mGson = new Gson();
|
||||
|
||||
private NfcAdapter mNfcAdapter;
|
||||
private PendingIntent mPendingIntent;
|
||||
private Tag mTag;
|
||||
private int mOperationId;
|
||||
|
||||
@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.setLifecycleOwner(this);
|
||||
mBinding.setClick(new BtnClick());
|
||||
|
||||
// 获取NFC适配器实例
|
||||
@@ -124,10 +135,48 @@ public class WriteMessageActivity extends AppCompatActivity {
|
||||
// 提取第一条记录的文本
|
||||
NdefRecord record = ndefMessage.getRecords()[0];
|
||||
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) {
|
||||
Log.e("NFC", "NDEF解析失败" + e.getMessage());
|
||||
Log.e(TAG, "NDEF解析失败" + e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
ndef.close();
|
||||
@@ -138,21 +187,70 @@ public class WriteMessageActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void setContactInfo(ContactInfo contactInfo) {
|
||||
mBinding.setContactInfo(contactInfo);
|
||||
}
|
||||
|
||||
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("请输入电话");
|
||||
Editable nameEditable = mBinding.etName.getText();
|
||||
Editable phoneEditable = mBinding.etPhone.getText();
|
||||
Editable wxidEditable = mBinding.etWxid.getText();
|
||||
Editable qqEditable = mBinding.etQq.getText();
|
||||
Editable tagEditable = mBinding.etTag.getText();
|
||||
|
||||
if (TextUtils.isEmpty(nameEditable)) {
|
||||
Toaster.showLong("请输入姓名或昵称");
|
||||
return;
|
||||
}
|
||||
String phone = editable.toString();
|
||||
if (!phone.startsWith("+86"))
|
||||
phone = "+86" + phone;
|
||||
WriteMessageUtils.writePhoneToTag(mTag, phone);
|
||||
String name = nameEditable.toString();
|
||||
String phoneNumber = phoneEditable.toString();
|
||||
String wechatId = wxidEditable.toString();
|
||||
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) {
|
||||
@@ -160,15 +258,29 @@ public class WriteMessageActivity extends AppCompatActivity {
|
||||
Toaster.showLong("NFC卡片未连接");
|
||||
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) {
|
||||
Toaster.showLong("NFC卡片未连接");
|
||||
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;
|
||||
|
||||
public class WechatContact implements Serializable {
|
||||
public class ContactInfo implements Serializable {
|
||||
private static final long serialVersionUID = -5570426400839799887L;
|
||||
|
||||
public String name;
|
||||
public String phone;
|
||||
public String tag;
|
||||
public String wxid;
|
||||
public String qq;
|
||||
/**
|
||||
* 0 电话 1 短信 2 微信语音 3 微信视频
|
||||
* 1 电话 2 短信 3 微信语音 4 微信视频 5 QQ语音 6 QQ视频
|
||||
*/
|
||||
int operation;
|
||||
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.phone = phone;
|
||||
this.operation = 3;
|
||||
@@ -47,7 +49,6 @@ public class WechatContact implements Serializable {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
@@ -56,6 +57,22 @@ public class WechatContact implements Serializable {
|
||||
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() {
|
||||
return operation;
|
||||
}
|
||||
@@ -64,6 +81,14 @@ public class WechatContact implements Serializable {
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(int version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
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 {
|
||||
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
|
||||
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();
|
||||
Toaster.show("写入联系人成功");
|
||||
} catch (IOException | FormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Ndef ndef = Ndef.get(tag);
|
||||
ndef.connect();
|
||||
ndef.writeNdefMessage(message); // 写入NDEF消息
|
||||
ndef.close();
|
||||
|
||||
}
|
||||
|
||||
public static void writeUrlToTag(Tag tag, String url) {
|
||||
public static void writeUrlToTag(Tag tag, String url) throws Exception {
|
||||
// 确保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();
|
||||
Toaster.show("写入Url成功");
|
||||
} catch (IOException | FormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Ndef ndef = Ndef.get(tag);
|
||||
ndef.connect();
|
||||
ndef.writeNdefMessage(message);
|
||||
ndef.close();
|
||||
}
|
||||
|
||||
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链接)
|
||||
Uri uri = Uri.parse(url);
|
||||
Uri uri = Uri.parse(fixedUrl);
|
||||
NdefRecord httpRecord = NdefRecord.createUri(uri);
|
||||
|
||||
// 2. 创建Android应用包名Record
|
||||
// 2. 创建Android应用包名Record
|
||||
NdefRecord androidRecord = NdefRecord.createApplicationRecord(pkg);
|
||||
|
||||
// 3. 按顺序组合记录
|
||||
// 3. 按顺序组合记录
|
||||
NdefMessage message = new NdefMessage(new NdefRecord[]{httpRecord, androidRecord});
|
||||
|
||||
// 4. 写入NFC标签
|
||||
try {
|
||||
Ndef ndef = Ndef.get(tag); // 获取标签对象
|
||||
ndef.connect();
|
||||
ndef.writeNdefMessage(message);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "writeUrlToTagIos: " + e.getMessage());
|
||||
}
|
||||
|
||||
// 4. 写入NFC标签
|
||||
Ndef ndef = Ndef.get(tag); // 获取标签对象
|
||||
ndef.connect();
|
||||
ndef.writeNdefMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
type="com.ttstd.elderlyassistant.activity.nfc.NfcActivity.BtnClick" />
|
||||
|
||||
<variable
|
||||
name="wechatContact"
|
||||
type="com.ttstd.elderlyassistant.bean.WechatContact" />
|
||||
name="contactInfo"
|
||||
type="com.ttstd.elderlyassistant.bean.ContactInfo" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
@@ -24,10 +24,10 @@
|
||||
android:id="@+id/et_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:maxLength="15"
|
||||
android:hint="Name"
|
||||
android:inputType="text"
|
||||
android:text="@{wechatContact.name}"
|
||||
android:text="@{contactInfo.name}"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -38,12 +38,12 @@
|
||||
android:id="@+id/et_phone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:hint="phone"
|
||||
android:maxLength="15"
|
||||
android:hint="phoneNumber"
|
||||
android:inputType="phone"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="@{wechatContact.phone}"
|
||||
android:text="@{contactInfo.phone}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
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
|
||||
name="click"
|
||||
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>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
android:layout_height="match_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"
|
||||
<LinearLayout
|
||||
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
|
||||
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" />
|
||||
<RadioButton
|
||||
android:id="@+id/rb_wechat_video"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:checked="@{contactInfo.operation==4}"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="拨打微信视频" />
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{click::writeQqMusicForIos}"
|
||||
android:text="QQ音乐(兼容iPhone)"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/et_phone" />
|
||||
<RadioButton
|
||||
android:id="@+id/rb_qq_voice"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:checked="@{contactInfo.operation==5}"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
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>
|
||||
</layout>
|
||||
Reference in New Issue
Block a user