整合来自xiaoyuanhost/TranspondSms代码

v3.6.1
1,添加多重规则。
2,添加规则测试按钮。
3,转发规则发送方列表页面支持长按删除。
4,整合逻辑
This commit is contained in:
pppscn 2021-03-04 11:42:30 +08:00
parent d2a4c6217f
commit b40382d2c2
12 changed files with 1047 additions and 207 deletions

View File

@ -2,13 +2,15 @@ package com.idormy.sms.forwarder;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
@ -19,13 +21,18 @@ import androidx.appcompat.app.AppCompatActivity;
import com.idormy.sms.forwarder.adapter.RuleAdapter;
import com.idormy.sms.forwarder.model.RuleModel;
import com.idormy.sms.forwarder.model.SenderModel;
import com.idormy.sms.forwarder.model.vo.SmsVo;
import com.idormy.sms.forwarder.utils.RuleUtil;
import com.idormy.sms.forwarder.utils.SendUtil;
import com.idormy.sms.forwarder.utils.SenderUtil;
import com.umeng.analytics.MobclickAgent;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static com.idormy.sms.forwarder.SenderActivity.NOTIFY;
public class RuleActivity extends AppCompatActivity {
private String TAG = "RuleActivity";
@ -35,11 +42,23 @@ public class RuleActivity extends AppCompatActivity {
private Long selectSenderId = 0l;
private String selectSenderName = "";
//消息处理者,创建一个Handler的子类对象,目的是重写Handler的处理消息的方法(handleMessage())
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case NOTIFY:
Toast.makeText(RuleActivity.this, msg.getData().getString("DATA"), Toast.LENGTH_LONG).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "oncreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sender);
setContentView(R.layout.activity_rule);
RuleUtil.init(RuleActivity.this);
SenderUtil.init(RuleActivity.this);
@ -48,7 +67,7 @@ public class RuleActivity extends AppCompatActivity {
adapter = new RuleAdapter(RuleActivity.this, R.layout.rule_item, ruleModels);
// 将适配器上的数据传递给listView
ListView listView = findViewById(R.id.list_view_sender);
ListView listView = findViewById(R.id.list_view_rule);
listView.setAdapter(adapter);
// 为ListView注册一个监听器当用户点击了ListView中的任何一个子项时就会回调onItemClick()方法
@ -61,7 +80,39 @@ public class RuleActivity extends AppCompatActivity {
setRule(ruleModel);
}
});
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {
//定义AlertDialog.Builder对象当长按列表项的时候弹出确认删除对话框
AlertDialog.Builder builder = new AlertDialog.Builder(RuleActivity.this);
builder.setMessage("确定删除?");
builder.setTitle("提示");
//添加AlertDialog.Builder对象的setPositiveButton()方法
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
RuleUtil.delRule(ruleModels.get(position).getId());
initRules();
adapter.del(ruleModels);
Toast.makeText(getBaseContext(), "删除列表项", Toast.LENGTH_SHORT).show();
}
});
//添加AlertDialog.Builder对象的setNegativeButton()方法
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create().show();
return true;
}
});
}
// 初始化数据
@ -69,49 +120,21 @@ public class RuleActivity extends AppCompatActivity {
ruleModels = RuleUtil.getRule(null, null);
}
public void addSender(View view) {
public void addRule(View view) {
setRule(null);
}
private void setRule(final RuleModel ruleModel) {
final AlertDialog.Builder alertDialog71 = new AlertDialog.Builder(RuleActivity.this);
final View view1 = View.inflate(RuleActivity.this, R.layout.activity_alter_dialog_setview_rule, null);
final RadioGroup radioGroupRuleFiled = (RadioGroup) view1.findViewById(R.id.radioGroupRuleFiled);
final LinearLayout matchTypeLayout = (LinearLayout) view1.findViewById(R.id.matchTypeLayout);
final LinearLayout matchValueLayout = (LinearLayout) view1.findViewById(R.id.matchValueLayout);
if (ruleModel != null) {
int id = ruleModel.getRuleFiledCheckId();
radioGroupRuleFiled.check(id);
if (id != 0) {
matchTypeLayout.setVisibility(View.GONE);
matchValueLayout.setVisibility(View.GONE);
} else {
matchTypeLayout.setVisibility(View.VISIBLE);
matchValueLayout.setVisibility(View.VISIBLE);
}
}
radioGroupRuleFiled.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
//Toast.makeText(RuleActivity.this, "Checked" + checkedId, Toast.LENGTH_LONG).show();
switch (checkedId) {
case R.id.btnTranspondAll:
matchTypeLayout.setVisibility(View.GONE);
matchValueLayout.setVisibility(View.GONE);
break;
default:
matchTypeLayout.setVisibility(View.VISIBLE);
matchValueLayout.setVisibility(View.VISIBLE);
break;
}
}
});
if (ruleModel != null) radioGroupRuleFiled.check(ruleModel.getRuleFiledCheckId());
final RadioGroup radioGroupRuleCheck = (RadioGroup) view1.findViewById(R.id.radioGroupRuleCheck);
if (ruleModel != null) radioGroupRuleCheck.check(ruleModel.getRuleCheckCheckId());
final TextView tv_mu_rule_tips = (TextView) view1.findViewById(R.id.tv_mu_rule_tips);
final TextView ruleSenderTv = (TextView) view1.findViewById(R.id.ruleSenderTv);
if (ruleModel != null && ruleModel.getSenderId() != null) {
List<SenderModel> getSeners = SenderUtil.getSender(ruleModel.getSenderId(), null);
@ -124,7 +147,7 @@ public class RuleActivity extends AppCompatActivity {
btSetRuleSender.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//Toast.makeText(RuleActivity.this, "selectSender", Toast.LENGTH_LONG).show();
Toast.makeText(RuleActivity.this, "selectSender", Toast.LENGTH_LONG).show();
selectSender(ruleSenderTv);
}
});
@ -133,8 +156,12 @@ public class RuleActivity extends AppCompatActivity {
if (ruleModel != null)
editTextRuleValue.setText(ruleModel.getValue());
//当更新选择的字段的时候更新之下各个选项的状态
refreshSelectRadioGroupRuleFiled(radioGroupRuleFiled, radioGroupRuleCheck, editTextRuleValue, tv_mu_rule_tips);
Button buttonruleok = view1.findViewById(R.id.buttonruleok);
Button buttonruledel = view1.findViewById(R.id.buttonruledel);
Button buttonruletest = view1.findViewById(R.id.buttonruletest);
alertDialog71
.setTitle(R.string.setrule)
.setView(view1)
@ -183,6 +210,78 @@ public class RuleActivity extends AppCompatActivity {
show.dismiss();
}
});
buttonruletest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Object senderId = ruleSenderTv.getTag();
if (senderId == null) {
Toast.makeText(RuleActivity.this, "请先创建选择发送方", Toast.LENGTH_LONG).show();
} else {
if (ruleModel == null) {
RuleModel newRuleModel = new RuleModel();
newRuleModel.setFiled(RuleModel.getRuleFiledFromCheckId(radioGroupRuleFiled.getCheckedRadioButtonId()));
newRuleModel.setCheck(RuleModel.getRuleCheckFromCheckId(radioGroupRuleCheck.getCheckedRadioButtonId()));
newRuleModel.setValue(editTextRuleValue.getText().toString());
newRuleModel.setSenderId(Long.valueOf(senderId.toString()));
testRule(newRuleModel, Long.valueOf(senderId.toString()));
} else {
ruleModel.setFiled(RuleModel.getRuleFiledFromCheckId(radioGroupRuleFiled.getCheckedRadioButtonId()));
ruleModel.setCheck(RuleModel.getRuleCheckFromCheckId(radioGroupRuleCheck.getCheckedRadioButtonId()));
ruleModel.setValue(editTextRuleValue.getText().toString());
ruleModel.setSenderId(Long.valueOf(senderId.toString()));
testRule(ruleModel, Long.valueOf(senderId.toString()));
}
}
}
});
}
//当更新选择的字段的时候更新之下各个选项的状态
// 如果设置了转发全部禁用选择模式和匹配值输入
// 如果设置了多重规则选择模式置为是
private void refreshSelectRadioGroupRuleFiled(RadioGroup radioGroupRuleFiled, final RadioGroup radioGroupRuleCheck, final EditText editTextRuleValue, final TextView tv_mu_rule_tips) {
refreshSelectRadioGroupRuleFiledAction(radioGroupRuleFiled.getCheckedRadioButtonId(), radioGroupRuleCheck, editTextRuleValue, tv_mu_rule_tips);
radioGroupRuleFiled.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
refreshSelectRadioGroupRuleFiledAction(checkedId, radioGroupRuleCheck, editTextRuleValue, tv_mu_rule_tips);
}
});
}
private void refreshSelectRadioGroupRuleFiledAction(int checkedRuleFiledId, final RadioGroup radioGroupRuleCheck, final EditText editTextRuleValue, final TextView tv_mu_rule_tips) {
tv_mu_rule_tips.setVisibility(View.GONE);
switch (checkedRuleFiledId) {
case R.id.btnTranspondAll:
for (int i = 0; i < radioGroupRuleCheck.getChildCount(); i++) {
((RadioButton) radioGroupRuleCheck.getChildAt(i)).setEnabled(false);
}
editTextRuleValue.setEnabled(false);
break;
case R.id.btnMultiMatch:
for (int i = 0; i < radioGroupRuleCheck.getChildCount(); i++) {
((RadioButton) radioGroupRuleCheck.getChildAt(i)).setEnabled(false);
}
editTextRuleValue.setEnabled(true);
tv_mu_rule_tips.setVisibility(View.VISIBLE);
break;
default:
for (int i = 0; i < radioGroupRuleCheck.getChildCount(); i++) {
((RadioButton) radioGroupRuleCheck.getChildAt(i)).setEnabled(true);
}
editTextRuleValue.setEnabled(true);
break;
}
}
public void selectSender(final TextView showTv) {
@ -208,13 +307,39 @@ public class RuleActivity extends AppCompatActivity {
builder.show();
}
public void testRule(final RuleModel ruleModel, final Long senderId) {
final View view = View.inflate(RuleActivity.this, R.layout.activity_alter_dialog_setview_rule_test, null);
final EditText editTextTestPhone = (EditText) view.findViewById(R.id.editTextTestPhone);
final EditText editTextTestMsgContent = (EditText) view.findViewById(R.id.editTextTestMsgContent);
Button buttonruletest = view.findViewById(R.id.buttonruletest);
AlertDialog.Builder ad1 = new AlertDialog.Builder(RuleActivity.this);
ad1.setTitle("测试规则");
ad1.setIcon(android.R.drawable.ic_dialog_info);
ad1.setView(view);
buttonruletest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("editTextTestPhone", editTextTestPhone.getText().toString());
Log.i("editTextTestMsgContent", editTextTestMsgContent.getText().toString());
try {
SmsVo testSmsVo = new SmsVo(editTextTestPhone.getText().toString(), editTextTestMsgContent.getText().toString(), new Date(), editTextTestPhone.getText().toString());
SendUtil.sendMsgByRuleModelSenderId(handler, ruleModel, testSmsVo, senderId);
} catch (Exception e) {
Toast.makeText(RuleActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
});
ad1.show();// 显示对话框
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy");
super.onDestroy();
}
@Override
protected void onResume() {
super.onResume();

View File

@ -115,7 +115,39 @@ public class SenderActivity extends AppCompatActivity {
}
});
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {
//定义AlertDialog.Builder对象当长按列表项的时候弹出确认删除对话框
AlertDialog.Builder builder = new AlertDialog.Builder(SenderActivity.this);
builder.setMessage("确定删除?");
builder.setTitle("提示");
//添加AlertDialog.Builder对象的setPositiveButton()方法
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SenderUtil.delSender(senderModels.get(position).getId());
initSenders();
adapter.del(senderModels);
Toast.makeText(getBaseContext(), "删除列表项", Toast.LENGTH_SHORT).show();
}
});
//添加AlertDialog.Builder对象的setNegativeButton()方法
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create().show();
return true;
}
});
}
// 初始化数据

View File

@ -1,15 +1,30 @@
package com.idormy.sms.forwarder.model;
import android.util.Log;
import com.idormy.sms.forwarder.R;
import com.idormy.sms.forwarder.model.vo.SmsVo;
import com.idormy.sms.forwarder.utils.RuleLineUtils;
import java.util.HashMap;
import java.util.Map;
public class RuleModel {
private String TAG = "RuleModel";
private Long id;
public static final String FILED_TRANSPOND_ALL = "transpond_all";
public static final String FILED_PHONE_NUM = "phone_num";
public static final String FILED_MSG_CONTENT = "msg_content";
public static final String FILED_MULTI_MATCH="multi_match";
public static final Map<String, String> FILED_MAP = new HashMap<String, String>();
static{
FILED_MAP.put("transpond_all", "转发全部");
FILED_MAP.put("phone_num", "手机号");
FILED_MAP.put("msg_content", "内容");
FILED_MAP.put("multi_match", "多重匹配");
}
private String filed;
public static final String CHECK_IS = "is";
public static final String CHECK_CONTAIN = "contain";
public static final String CHECK_START_WITH = "startwith";
@ -17,12 +32,6 @@ public class RuleModel {
public static final String CHECK_NOT_IS = "notis";
public static final Map<String, String> CHECK_MAP = new HashMap<String, String>();
static {
FILED_MAP.put("transpond_all", "全部");
FILED_MAP.put("phone_num", "手机号");
FILED_MAP.put("msg_content", "内容");
}
static {
CHECK_MAP.put("is", "");
CHECK_MAP.put("contain", "包含");
@ -30,9 +39,6 @@ public class RuleModel {
CHECK_MAP.put("endwith", "结尾是");
CHECK_MAP.put("notis", "不是");
}
private Long id;
private String filed;
private String check;
private String value;
@ -40,6 +46,83 @@ public class RuleModel {
private Long senderId;
private Long time;
//字段分支
public boolean checkMsg(SmsVo msg) throws Exception {
//检查这一行和上一行合并的结果是否命中
boolean mixChecked=false;
if(msg!=null){
//先检查规则是否命中
switch (this.filed){
case FILED_TRANSPOND_ALL:
mixChecked= true;
break;
case FILED_PHONE_NUM:
mixChecked= checkValue(msg.getMobile());
break;
case FILED_MSG_CONTENT:
mixChecked= checkValue(msg.getContent());
break;
case FILED_MULTI_MATCH:
mixChecked= RuleLineUtils.checkRuleLines(msg,this.value);
break;
default:
break;
}
}
Log.i(TAG, "rule:"+this+" checkMsg:"+msg+" checked:"+mixChecked);
return mixChecked;
}
//内容分支
public boolean checkValue(String msgValue){
boolean checked=false;
if(this.value!=null){
switch (this.check){
case CHECK_IS:
checked=this.value.equals(msgValue);
break;
case CHECK_CONTAIN:
if(msgValue!=null){
checked=msgValue.contains(this.value);
}
break;
case CHECK_START_WITH:
if(msgValue!=null){
checked=msgValue.startsWith(this.value);
}
break;
case CHECK_END_WITH:
if(msgValue!=null){
checked=msgValue.endsWith(this.value);
}
break;
default:
break;
}
}
Log.i(TAG, "checkValue "+msgValue+" "+this.check+" "+this.value+" checked:"+checked);
return checked;
}
public String getRuleMatch() {
switch (filed){
case FILED_TRANSPOND_ALL:
return "全部转发到 ";
default:
return ""+FILED_MAP.get(filed)+" "+CHECK_MAP.get(check)+" "+value+" 转发到 ";
}
}
public static String getRuleMatch(String filed, String check, String value) {
switch (filed) {
case FILED_TRANSPOND_ALL:
@ -51,19 +134,53 @@ public class RuleModel {
}
public static String getRuleFiledFromCheckId(int id) {
switch (id) {
public Long getRuleSenderId() {
return senderId;
}
public int getRuleFiledCheckId(){
switch (filed){
case FILED_MSG_CONTENT:
return R.id.btnContent;
case FILED_PHONE_NUM:
return R.id.btnPhone;
case FILED_MULTI_MATCH:
return R.id.btnMultiMatch;
default:
return R.id.btnTranspondAll;
}
}
public static String getRuleFiledFromCheckId(int id){
switch (id){
case R.id.btnContent:
return FILED_MSG_CONTENT;
case R.id.btnPhone:
return FILED_PHONE_NUM;
case R.id.btnMultiMatch:
return FILED_MULTI_MATCH;
default:
return FILED_TRANSPOND_ALL;
}
}
public static String getRuleCheckFromCheckId(int id) {
switch (id) {
public int getRuleCheckCheckId(){
switch (check){
case CHECK_CONTAIN:
return R.id.btnContain;
case CHECK_START_WITH:
return R.id.btnStartWith;
case CHECK_END_WITH:
return R.id.btnEndWith;
case CHECK_NOT_IS:
return R.id.btnNotIs;
default:
return R.id.btnIs;
}
}
public static String getRuleCheckFromCheckId(int id){
switch (id){
case R.id.btnContain:
return CHECK_CONTAIN;
case R.id.btnStartWith:
@ -125,45 +242,6 @@ public class RuleModel {
this.value = value;
}
public String getRuleMatch() {
switch (filed) {
case FILED_TRANSPOND_ALL:
return "全部转发到 ";
default:
return "" + FILED_MAP.get(filed) + " " + CHECK_MAP.get(check) + " " + value + " 转发到 ";
}
}
public Long getRuleSenderId() {
return senderId;
}
public int getRuleFiledCheckId() {
switch (filed) {
case FILED_MSG_CONTENT:
return R.id.btnContent;
case FILED_PHONE_NUM:
return R.id.btnPhone;
default:
return R.id.btnTranspondAll;
}
}
public int getRuleCheckCheckId() {
switch (check) {
case CHECK_CONTAIN:
return R.id.btnContain;
case CHECK_START_WITH:
return R.id.btnStartWith;
case CHECK_END_WITH:
return R.id.btnEndWith;
case CHECK_NOT_IS:
return R.id.btnNotIs;
default:
return R.id.btnIs;
}
}
@Override
public String toString() {

View File

@ -0,0 +1,423 @@
package com.idormy.sms.forwarder.utils;
import android.util.Log;
import com.idormy.sms.forwarder.model.vo.SmsVo;
import java.util.ArrayList;
import java.util.List;
class RuleLine {
static String TAG = "RuleLine";
static Boolean STARTLOG = true;
//开头有几个空格
int headSpaceNum = 0;
RuleLine beforeRuleLine;
RuleLine nextRuleLine;
RuleLine parentRuleLine;
RuleLine childRuleLine;
//and or
String conjunction;
public static List<String> CONJUNCTION_LIST = new ArrayList<String>();
public static final String CONJUNCTION_AND = "并且";
public static final String CONJUNCTION_OR = "或者";
static {
CONJUNCTION_LIST.add("and");
CONJUNCTION_LIST.add("or");
CONJUNCTION_LIST.add("并且");
CONJUNCTION_LIST.add("或者");
}
//手机号 短信内容
String field;
public static List<String> FILED_LIST = new ArrayList<String>();
public static final String FILED_PHONE_NUM = "手机号";
public static final String FILED_MSG_CONTENT = "短信内容";
static {
FILED_LIST.add("手机号");
FILED_LIST.add("短信内容");
}
// 是否
String sure;
public static List<String> SURE_LIST = new ArrayList<String>();
public static final String SURE_YES = "";
public static final String SURE_NOT = "不是";
static {
SURE_LIST.add("");
SURE_LIST.add("不是");
}
String check;
public static List<String> CHECK_LIST = new ArrayList<String>();
public static final String CHECK_EQUALS = "相等";
public static final String CHECK_CONTAIN = "包含";
public static final String CHECK_START_WITH = "开头";
public static final String CHECK_END_WITH = "结尾";
static {
CHECK_LIST.add("相等");
CHECK_LIST.add("包含");
CHECK_LIST.add("开头");
CHECK_LIST.add("结尾");
}
String value;
//字段分支
public boolean checkMsg(SmsVo msg) {
//检查这一行和上一行合并的结果是否命中
boolean mixChecked = false;
//先检查规则是否命中
switch (this.field) {
case FILED_PHONE_NUM:
mixChecked = checkValue(msg.getMobile());
break;
case FILED_MSG_CONTENT:
mixChecked = checkValue(msg.getContent());
break;
default:
break;
}
//整合肯定词
switch (this.sure) {
case SURE_YES:
mixChecked = mixChecked;
break;
case SURE_NOT:
mixChecked = !mixChecked;
break;
default:
mixChecked = false;
break;
}
logg("rule:" + this + " checkMsg:" + msg + " checked:" + mixChecked);
return mixChecked;
}
//内容分支
public boolean checkValue(String msgValue) {
boolean checked = false;
switch (this.check) {
case CHECK_EQUALS:
checked = this.value.equals(msgValue);
break;
case CHECK_CONTAIN:
if (msgValue != null) {
checked = msgValue.contains(this.value);
}
break;
case CHECK_START_WITH:
if (msgValue != null) {
checked = msgValue.startsWith(this.value);
}
break;
case CHECK_END_WITH:
if (msgValue != null) {
checked = msgValue.endsWith(this.value);
}
break;
default:
break;
}
logg("checkValue " + msgValue + " " + this.check + " " + this.value + " checked:" + checked);
return checked;
}
public RuleLine(String line, int linenum, RuleLine beforeRuleLine) throws Exception {
logg("----------" + linenum + "-----------------");
logg(line);
//规则检验
//并且 手机号 相等 10086
//[并且, , 手机号, 相等, 10086]
// 并且 内容 包含 asfas
//[, , 并且, , 内容, 包含, asfas]
//处理头空格数用来确认跟上一行节点的相对位置是同级还是子级
//处理4个字段之后的全部当做value
//标记3个阶段
boolean isCountHeading = false;
boolean isDealMiddel = false;
boolean isDealValue = false;
//用于保存4个中间体 并且, , 内容, 包含
List<String> middleList = new ArrayList<>(4);
//保存每个中间体字符串
StringBuilder buildMiddleWord = new StringBuilder();
StringBuilder valueBuilder = new StringBuilder();
for (int i = 0; i < line.length(); i++) {
String w = String.valueOf(line.charAt(i));
logg("walk over:" + w);
//控制阶段
//开始处理头
if (i == 0) {
if (" ".equals(w)) {
logg("start to isCountHeading:");
isCountHeading = true;
} else {
//直接进入处理中间体阶段
isCountHeading = false;
isDealMiddel = true;
logg("start to isDealMiddel:");
}
}
//正在数空格头但是遇到非空格阶段变更:由处理空头阶段 变为 处理 中间体阶段
if (isCountHeading && (!" ".equals(w))) {
logg("isCountHeading to isDealMiddel:");
isCountHeading = false;
isDealMiddel = true;
}
//正在处理中间体中间体数量够了阶段变更由处理中间体 变为 处理 value
if (isDealMiddel && middleList.size() == 4) {
logg("isDealMiddel done middleList:" + middleList);
logg("isDealMiddel to isDealValue:");
isDealMiddel = false;
isDealValue = true;
}
logg("isCountHeading:" + isCountHeading);
logg("isDealMiddel:" + isDealMiddel);
logg("isDealValue:" + isDealValue);
if (isCountHeading) {
if (" ".equals(w)) {
logg("headSpaceNum++:" + headSpaceNum);
headSpaceNum++;
}
}
if (isDealMiddel) {
//遇到空格
if (" ".equals(w)) {
if (buildMiddleWord.length() == 0) {
throw new Exception(linenum + "行:语法错误不允许出现连续空格!");
} else {
//生成了一个中间体
middleList.add(buildMiddleWord.toString());
logg("get Middle++:" + buildMiddleWord.toString());
buildMiddleWord = new StringBuilder();
}
} else {
//把w拼接到中间体上
buildMiddleWord.append(w);
logg("buildMiddleWord length:" + buildMiddleWord.length() + "buildMiddleWord:" + buildMiddleWord.toString());
}
}
if (isDealValue) {
//把余下的所有字符都拼接给value
valueBuilder.append(w);
}
}
logg("isDealValue done valueBuilder:" + valueBuilder.toString());
if (middleList.size() != 4) {
throw new Exception(linenum + "行配置错误每行必须有4段组成例如 并且 手机号 是 相等 ");
}
//规则对齐
if (beforeRuleLine != null) {
logg("beforeRuleLine :" + beforeRuleLine);
logg("thisRuleLine :" + this);
//同级别
if (headSpaceNum == beforeRuleLine.headSpaceNum) {
logg("同级别");
this.beforeRuleLine = beforeRuleLine;
beforeRuleLine.nextRuleLine = this;
}
//子级
if (headSpaceNum - 1 == beforeRuleLine.headSpaceNum) {
logg("子级");
this.parentRuleLine = beforeRuleLine;
beforeRuleLine.childRuleLine = this;
}
//查找父级别
if (headSpaceNum < beforeRuleLine.headSpaceNum) {
//匹配到最近一个同级
RuleLine fBeforeRuleLine = beforeRuleLine.getBeforeRuleLine();
if (fBeforeRuleLine == null) {
fBeforeRuleLine = beforeRuleLine.getParentRuleLine();
}
while (fBeforeRuleLine != null) {
logg("fBeforeRuleLine" + fBeforeRuleLine);
//查找到同级别
if (headSpaceNum == fBeforeRuleLine.headSpaceNum) {
logg("父级别");
this.beforeRuleLine = fBeforeRuleLine;
fBeforeRuleLine.nextRuleLine = this;
break;
} else {
//向上查找
RuleLine testfBeforeRuleLine = fBeforeRuleLine.getBeforeRuleLine();
if (testfBeforeRuleLine == null) {
testfBeforeRuleLine = fBeforeRuleLine.getParentRuleLine();
}
fBeforeRuleLine = testfBeforeRuleLine;
}
}
}
} else {
logg("根级别");
}
this.conjunction = middleList.get(0);
this.sure = middleList.get(1);
this.field = middleList.get(2);
this.check = middleList.get(3);
this.value = valueBuilder.toString();
if (!CONJUNCTION_LIST.contains(this.conjunction)) {
throw new Exception(linenum + "行配置错误:连接词只支持:" + CONJUNCTION_LIST + " 但提供了" + this.conjunction);
}
if (!FILED_LIST.contains(this.field)) {
throw new Exception(linenum + "行配置错误:字段只支持:" + FILED_LIST + " 但提供了" + this.field);
}
if (!SURE_LIST.contains(this.sure)) {
throw new Exception(linenum + "行配置错误 " + this.sure + " 确认词只支持:" + SURE_LIST + " 但提供了" + this.sure);
}
if (!CHECK_LIST.contains(this.check)) {
throw new Exception(linenum + "行配置错误:比较词只支持:" + CHECK_LIST + " 但提供了" + this.check);
}
logg("----------" + linenum + "==" + this);
}
public static void startLog(boolean startLog) {
STARTLOG = startLog;
}
public static void logg(String msg) {
if (STARTLOG) {
Log.i(TAG, msg);
}
}
@Override
public String toString() {
return "RuleLine{" +
"headSpaceNum='" + headSpaceNum + '\'' +
"conjunction='" + conjunction + '\'' +
", field='" + field + '\'' +
", sure='" + sure + '\'' +
", check='" + check + '\'' +
", value='" + value + '\'' +
'}';
}
public int getHeadSpaceNum() {
return headSpaceNum;
}
public void setHeadSpaceNum(int headSpaceNum) {
this.headSpaceNum = headSpaceNum;
}
public RuleLine getBeforeRuleLine() {
return beforeRuleLine;
}
public void setBeforeRuleLine(RuleLine beforeRuleLine) {
this.beforeRuleLine = beforeRuleLine;
}
public RuleLine getNextRuleLine() {
return nextRuleLine;
}
public void setNextRuleLine(RuleLine nextRuleLine) {
this.nextRuleLine = nextRuleLine;
}
public RuleLine getParentRuleLine() {
return parentRuleLine;
}
public void setParentRuleLine(RuleLine parentRuleLine) {
this.parentRuleLine = parentRuleLine;
}
public RuleLine getChildRuleLine() {
return childRuleLine;
}
public void setChildRuleLine(RuleLine childRuleLine) {
this.childRuleLine = childRuleLine;
}
public String getConjunction() {
return conjunction;
}
public void setConjunction(String conjunction) {
this.conjunction = conjunction;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public String getSure() {
return sure;
}
public void setSure(String sure) {
this.sure = sure;
}
public String getCheck() {
return check;
}
public void setCheck(String check) {
this.check = check;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@ -0,0 +1,134 @@
package com.idormy.sms.forwarder.utils;
import android.util.Log;
import com.idormy.sms.forwarder.model.vo.SmsVo;
import java.util.Date;
import java.util.Scanner;
import static com.idormy.sms.forwarder.utils.RuleLine.CONJUNCTION_AND;
import static com.idormy.sms.forwarder.utils.RuleLine.CONJUNCTION_OR;
public class RuleLineUtils {
static String TAG = "RuleLineUtils";
static Boolean STARTLOG = false;
public static void main(String[] args) throws Exception {
String a = "并且 是 手机号 相等 10086\n" +
" 或者 是 手机号 结尾 哈哈哈\n" +
" 并且 是 短信内容 包含 asfas\n" +
" 或者 是 手机号 结尾 aaaa\n" +
"并且 是 手机号 相等 100861\n" +
"并且 是 手机号 相等 100861";
SmsVo msg = new SmsVo("10086", "哈哈哈", new Date(), "15888888888");
logg("check:" + checkRuleLines(msg, a));
}
public static void startLog(boolean startLog) {
STARTLOG = startLog;
}
public static void logg(String msg) {
if (STARTLOG) {
Log.i(TAG, msg);
}
}
public static boolean checkRuleLines(SmsVo msg, String RuleLines) throws Exception {
Scanner scanner = new Scanner(RuleLines);
int linenum = 0;
RuleLine headRuleLine = null;
RuleLine beforeRuleLine = null;
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
logg(linenum + " : " + line);
//第一行
if (linenum == 0) {
//第一行不允许缩进
if (line.startsWith(" ")) {
throw new Exception("第一行不允许缩进");
}
}
// process the line
beforeRuleLine = RuleLineUtils.generateRuleTree(line, linenum, beforeRuleLine);
if (linenum == 0) {
headRuleLine = beforeRuleLine;
}
linenum++;
}
return checkRuleTree(msg, headRuleLine);
}
/**
* 使用规则树判断消息是否命中规则
* Rule节点是否命中取决于该节点是否命中该节点子结点如果有的话是否命中该节点下节点如果有的话是否命中
* 递归检查
*/
public static boolean checkRuleTree(SmsVo msg, RuleLine currentRuleLine) throws Exception {
//该节点是否命中
boolean currentAll = currentRuleLine.checkMsg(msg);
logg("current:" + currentRuleLine + " checked:" + currentAll);
//该节点子结点如果有的话是否命中
if (currentRuleLine.getChildRuleLine() != null) {
logg(" child:" + currentRuleLine.getChildRuleLine());
//根据情况连接结果
switch (currentRuleLine.getChildRuleLine().conjunction) {
case CONJUNCTION_AND:
currentAll = currentAll && checkRuleTree(msg, currentRuleLine.getChildRuleLine());
break;
case CONJUNCTION_OR:
currentAll = currentAll || checkRuleTree(msg, currentRuleLine.getChildRuleLine());
break;
default:
throw new Exception("child wrong conjunction");
}
}
//该节点下节点如果有的话是否命中
if (currentRuleLine.getNextRuleLine() != null) {
logg("next:" + currentRuleLine.getNextRuleLine());
//根据情况连接结果
switch (currentRuleLine.getNextRuleLine().conjunction) {
case CONJUNCTION_AND:
currentAll = currentAll && checkRuleTree(msg, currentRuleLine.getNextRuleLine());
break;
case CONJUNCTION_OR:
currentAll = currentAll || checkRuleTree(msg, currentRuleLine.getNextRuleLine());
break;
default:
throw new Exception("next wrong conjunction");
}
}
return currentAll;
}
/**
* 生成规则树
* 一行代表一个规则
*/
public static RuleLine generateRuleTree(String line, int lineNum, RuleLine parentRuleLine) throws Exception {
String[] words = line.split(" ");
return new RuleLine(line, lineNum, parentRuleLine);
}
}

View File

@ -1,6 +1,7 @@
package com.idormy.sms.forwarder.utils;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import com.alibaba.fastjson.JSON;
@ -16,14 +17,6 @@ import com.idormy.sms.forwarder.model.vo.WebNotifySettingVo;
import java.util.List;
import static com.idormy.sms.forwarder.model.RuleModel.CHECK_CONTAIN;
import static com.idormy.sms.forwarder.model.RuleModel.CHECK_END_WITH;
import static com.idormy.sms.forwarder.model.RuleModel.CHECK_IS;
import static com.idormy.sms.forwarder.model.RuleModel.CHECK_NOT_IS;
import static com.idormy.sms.forwarder.model.RuleModel.CHECK_START_WITH;
import static com.idormy.sms.forwarder.model.RuleModel.FILED_MSG_CONTENT;
import static com.idormy.sms.forwarder.model.RuleModel.FILED_PHONE_NUM;
import static com.idormy.sms.forwarder.model.RuleModel.FILED_TRANSPOND_ALL;
import static com.idormy.sms.forwarder.model.SenderModel.TYPE_BARK;
import static com.idormy.sms.forwarder.model.SenderModel.TYPE_DINGDING;
import static com.idormy.sms.forwarder.model.SenderModel.TYPE_EMAIL;
@ -59,98 +52,53 @@ public class SendUtil {
Log.i(TAG, "send_msg smsVo:" + smsVo);
RuleUtil.init(context);
LogUtil.init(context);
//初始化 EmailKit
//EmailKit.initialize(context);
List<RuleModel> rulelist = RuleUtil.getRule(null, null);
if (!rulelist.isEmpty()) {
SenderUtil.init(context);
for (RuleModel ruleModel : rulelist
) {
boolean canSend = false;
//使用转发规则制定的字段 匹配
switch (ruleModel.getFiled()) {
case FILED_TRANSPOND_ALL:
canSend = true;
break;
case FILED_PHONE_NUM:
switch (ruleModel.getCheck()) {
case CHECK_IS:
if (smsVo.getMobile() != null && smsVo.getMobile().equals(ruleModel.getValue())) {
canSend = true;
}
break;
case CHECK_NOT_IS:
if (smsVo.getMobile() != null && !smsVo.getMobile().equals(ruleModel.getValue())) {
canSend = true;
}
break;
case CHECK_START_WITH:
if (smsVo.getMobile() != null && smsVo.getMobile().startsWith(ruleModel.getValue())) {
canSend = true;
}
break;
case CHECK_END_WITH:
if (smsVo.getMobile() != null && smsVo.getMobile().endsWith(ruleModel.getValue())) {
canSend = true;
}
break;
case CHECK_CONTAIN:
if (smsVo.getMobile() != null && smsVo.getMobile().contains(ruleModel.getValue())) {
canSend = true;
}
break;
}
break;
case FILED_MSG_CONTENT:
switch (ruleModel.getCheck()) {
case CHECK_IS:
if (smsVo.getContent() != null && smsVo.getContent().equals(ruleModel.getValue())) {
canSend = true;
}
break;
case CHECK_NOT_IS:
if (smsVo.getContent() != null && !smsVo.getContent().equals(ruleModel.getValue())) {
canSend = true;
}
break;
case CHECK_START_WITH:
if (smsVo.getContent() != null && smsVo.getContent().startsWith(ruleModel.getValue())) {
canSend = true;
}
break;
case CHECK_END_WITH:
if (smsVo.getContent() != null && smsVo.getContent().endsWith(ruleModel.getValue())) {
canSend = true;
}
break;
case CHECK_CONTAIN:
if (smsVo.getContent() != null && smsVo.getContent().contains(ruleModel.getValue())) {
canSend = true;
}
break;
}
break;
}
for (RuleModel ruleModel : rulelist) {
//规则匹配发现需要发送
if (canSend) {
List<SenderModel> senderModels = SenderUtil.getSender(ruleModel.getSenderId(), null);
for (SenderModel senderModel : senderModels
) {
LogUtil.addLog(new LogModel(smsVo.getMobile(), smsVo.getContent(), smsVo.getPhoneNumber(), senderModel.getId()));
SendUtil.senderSendMsg(smsVo, senderModel);
try {
if (ruleModel.checkMsg(smsVo)) {
List<SenderModel> senderModels = SenderUtil.getSender(ruleModel.getSenderId(), null);
for (SenderModel senderModel : senderModels
) {
LogUtil.addLog(new LogModel(smsVo.getMobile(), smsVo.getContent(), smsVo.getPhoneNumber(), senderModel.getId()));
SendUtil.senderSendMsgNoHandError(smsVo, senderModel);
}
}
} catch (Exception e) {
Log.e(TAG, "send_msg: fail checkMsg:", e);
}
}
}
//EmailKit.destroy();
}
public static void senderSendMsg(SmsVo smsVo, SenderModel senderModel) {
public static void sendMsgByRuleModelSenderId(final Handler handError, RuleModel ruleModel, SmsVo smsVo, Long senderId) throws Exception {
if (senderId == null) {
throw new Exception("先新建并选择发送方");
}
if (!ruleModel.checkMsg(smsVo)) {
throw new Exception("短信未匹配中规则");
}
List<SenderModel> senderModels = SenderUtil.getSender(senderId, null);
if (senderModels.isEmpty()) {
throw new Exception("未找到发送方");
}
for (SenderModel senderModel : senderModels
) {
SendUtil.senderSendMsg(handError, smsVo, senderModel);
}
}
public static void senderSendMsgNoHandError(SmsVo smsVo, SenderModel senderModel) {
SendUtil.senderSendMsg(null, smsVo, senderModel);
}
public static void senderSendMsg(Handler handError, SmsVo smsVo, SenderModel senderModel) {
Log.i(TAG, "senderSendMsg smsVo:" + smsVo + "senderModel:" + senderModel);
switch (senderModel.getType()) {
@ -160,7 +108,7 @@ public class SendUtil {
DingDingSettingVo dingDingSettingVo = JSON.parseObject(senderModel.getJsonSetting(), DingDingSettingVo.class);
if (dingDingSettingVo != null) {
try {
SenderDingdingMsg.sendMsg(null, dingDingSettingVo.getToken(), dingDingSettingVo.getSecret(), dingDingSettingVo.getAtMobils(), dingDingSettingVo.getAtAll(), smsVo.getSmsVoForSend());
SenderDingdingMsg.sendMsg(handError, dingDingSettingVo.getToken(), dingDingSettingVo.getSecret(), dingDingSettingVo.getAtMobils(), dingDingSettingVo.getAtAll(), smsVo.getSmsVoForSend());
} catch (Exception e) {
Log.e(TAG, "senderSendMsg: dingding error " + e.getMessage());
}
@ -175,7 +123,7 @@ public class SendUtil {
EmailSettingVo emailSettingVo = JSON.parseObject(senderModel.getJsonSetting(), EmailSettingVo.class);
if (emailSettingVo != null) {
try {
SenderMailMsg.sendEmail(null, emailSettingVo.getHost(), emailSettingVo.getPort(), emailSettingVo.getSsl(), emailSettingVo.getFromEmail(),
SenderMailMsg.sendEmail(handError, emailSettingVo.getHost(), emailSettingVo.getPort(), emailSettingVo.getSsl(), emailSettingVo.getFromEmail(),
emailSettingVo.getPwd(), emailSettingVo.getToEmail(), smsVo.getMobile(), smsVo.getSmsVoForSend());
} catch (Exception e) {
Log.e(TAG, "senderSendMsg: SenderMailMsg error " + e.getMessage());
@ -191,7 +139,7 @@ public class SendUtil {
WebNotifySettingVo webNotifySettingVo = JSON.parseObject(senderModel.getJsonSetting(), WebNotifySettingVo.class);
if (webNotifySettingVo != null) {
try {
SenderWebNotifyMsg.sendMsg(null, webNotifySettingVo.getToken(), webNotifySettingVo.getSecret(), smsVo.getMobile(), smsVo.getSmsVoForSend());
SenderWebNotifyMsg.sendMsg(handError, webNotifySettingVo.getToken(), webNotifySettingVo.getSecret(), smsVo.getMobile(), smsVo.getSmsVoForSend());
} catch (Exception e) {
Log.e(TAG, "senderSendMsg: SenderWebNotifyMsg error " + e.getMessage());
}
@ -206,7 +154,7 @@ public class SendUtil {
QYWXGroupRobotSettingVo qywxGroupRobotSettingVo = JSON.parseObject(senderModel.getJsonSetting(), QYWXGroupRobotSettingVo.class);
if (qywxGroupRobotSettingVo != null) {
try {
SenderQyWxGroupRobotMsg.sendMsg(null, qywxGroupRobotSettingVo.getWebHook(), smsVo.getMobile(), smsVo.getSmsVoForSend());
SenderQyWxGroupRobotMsg.sendMsg(handError, qywxGroupRobotSettingVo.getWebHook(), smsVo.getMobile(), smsVo.getSmsVoForSend());
} catch (Exception e) {
Log.e(TAG, "senderSendMsg: SenderQyWxGroupRobotMsg error " + e.getMessage());
}
@ -215,13 +163,14 @@ public class SendUtil {
}
break;
case TYPE_BARK:
//try phrase json setting
if (senderModel.getJsonSetting() != null) {
BarkSettingVo barkSettingVo = JSON.parseObject(senderModel.getJsonSetting(), BarkSettingVo.class);
if (barkSettingVo != null) {
try {
SenderBarkMsg.sendMsg(null, barkSettingVo.getServer(), smsVo.getMobile(), smsVo.getContent(), smsVo.getPhoneNumber());
SenderBarkMsg.sendMsg(handError, barkSettingVo.getServer(), smsVo.getMobile(), smsVo.getContent(), smsVo.getPhoneNumber());
} catch (Exception e) {
Log.e(TAG, "senderSendMsg: SenderBarkMsg error " + e.getMessage());
}

View File

@ -6,8 +6,9 @@ import android.preference.PreferenceManager;
import android.util.Log;
public class SettingUtil {
static Boolean hasInit = false;
private static String TAG = "SettingUtil";
static Boolean hasInit = false;
private static SharedPreferences sp_setting = null;
private static Context context = null;
@ -60,7 +61,7 @@ public class SettingUtil {
public static String get_send_util_email(String key) {
Log.d(TAG, "get_send_util_email key" + key);
String defaultstt = "";
if (key.equals(Define.SP_MSG_SEND_UTIL_EMAIL_HOST_KEY)) defaultstt = "stmp服务器";
if (key.equals(Define.SP_MSG_SEND_UTIL_EMAIL_HOST_KEY)) defaultstt = "smtp服务器";
if (key.equals(Define.SP_MSG_SEND_UTIL_EMAIL_PORT_KEY)) defaultstt = "端口";
if (key.equals(Define.SP_MSG_SEND_UTIL_EMAIL_FROMADD_KEY)) defaultstt = "发送邮箱";
if (key.equals(Define.SP_MSG_SEND_UTIL_EMAIL_PSW_KEY)) defaultstt = "密码";

View File

@ -44,7 +44,29 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="内容" />
<RadioButton
android:id="@+id/btnMultiMatch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="多重匹配" />
</RadioGroup>
<TextView
android:id="@+id/tv_mu_rule_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:text="
多重匹配规则如下:\n
\n
并且 是 手机号 相等 10086\n
或者 是 手机号 相等 10011\n
并且 是 短信内容 包含 欠费\n
\n
已上规则表示:收到短信,并且手机号是10086 或者 手机号是10010 并且短信内容包含欠费 时转发短信\n
注意每行开始的空格代表层级,太过复杂的多重规则可能导致内存占用很大
" />
</LinearLayout>
<LinearLayout
@ -53,8 +75,7 @@
android:layout_height="fill_parent"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:orientation="vertical"
android:visibility="gone">
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
@ -107,8 +128,7 @@
android:layout_height="fill_parent"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:orientation="vertical"
android:visibility="gone">
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
@ -182,6 +202,14 @@
android:layout_weight="1"
android:text="@string/del" />
<Button
android:id="@+id/buttonruletest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_weight="1"
android:text="@string/test" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:text="测试模拟的手机号" />
<EditText
android:id="@+id/editTextTestPhone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:ems="10"
android:inputType=""
android:text="" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:text="测试模拟的短信内容" />
<EditText
android:id="@+id/editTextTestMsgContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:ems="10"
android:inputType=""
android:text="" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<Button
android:id="@+id/buttonruletest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_weight="1"
android:text="@string/test" />
</LinearLayout>
</LinearLayout>

View File

@ -5,6 +5,14 @@
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:gravity="center"
android:text="@string/rule_tips"
android:textColor="@android:color/holo_blue_dark" />
<ListView
android:id="@+id/list_view_rule"
android:layout_width="match_parent"
@ -19,6 +27,7 @@
android:layout_margin="10px"
android:layout_weight="9"
android:background="@android:color/holo_blue_dark"
android:onClick="addRule"
android:text="添加转发规则"
tools:ignore="NestedWeights" />

View File

@ -5,30 +5,29 @@
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:gravity="center"
android:text="@string/sender_tips"
android:textColor="@android:color/holo_blue_dark" />
<ListView
android:id="@+id/list_view_sender"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:layout_margin="4dp"
android:layout_weight="1" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
<Button
android:id="@+id/button22"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10px"
android:layout_weight="9"
android:orientation="horizontal">
<Button
android:id="@+id/button22"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:layout_weight="1"
android:background="@android:color/holo_blue_dark"
android:onClick="addSender"
android:text="添加"
tools:ignore="NestedWeights" />
</LinearLayout>
android:background="@android:color/holo_blue_dark"
android:onClick="addSender"
android:text="添加发送方"
tools:ignore="NestedWeights" />
</LinearLayout>

View File

@ -13,4 +13,6 @@
<string name="setbarktitle">设置Bark</string>
<string name="check_new_version">检查更新</string>
<string name="to_setting">设置</string>
<string name="rule_tips">Tip:“添加转发规则”新建,长按删除,点击进行更新</string>
<string name="sender_tips">Tip:“添加发送方”新建,长按删除,点击进行更新</string>
</resources>