first commit

This commit is contained in:
2023-12-06 20:04:47 +08:00
commit bce11daee7
48 changed files with 1457 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
package com.nbee.echolink;
import android.os.Build;
public class DeviceInfo {
public static int getApiVersion() {
return Build.VERSION.SDK_INT;
}
}

View File

@@ -0,0 +1,28 @@
package com.nbee.echolink;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import com.nbee.echolink.broadcast.PhoneCallReceiver;
import com.nbee.echolink.broadcast.SMSReceiver;
import com.nbee.echolink.service.MonitorService;
public class MainActivity extends AppCompatActivity {
private final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 启动MonitorService服务
Intent serviceIntent = new Intent(this, MonitorService.class);
startService(serviceIntent);
}
}

View File

@@ -0,0 +1,18 @@
package com.nbee.echolink.broadcast;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.nbee.echolink.service.MonitorService;
public class BootCompletedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
// 启动您的服务或活动
Intent serviceIntent = new Intent(context, MonitorService.class);
context.startService(serviceIntent);
}
}
}

View File

@@ -0,0 +1,32 @@
package com.nbee.echolink.broadcast;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import com.nbee.echolink.service.MonitorService;
public class PhoneCallReceiver extends BroadcastReceiver {
private static final String TAG = "PhoneCallReceiver";
@Override
public void onReceive(Context context, Intent intent) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (tm != null) {
tm.listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
if (state == TelephonyManager.CALL_STATE_RINGING) {
// 来电号码
Intent serviceIntent = new Intent(context, MonitorService.class);
serviceIntent.putExtra("incomingNumber", incomingNumber);
context.startService(serviceIntent);
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}
}
}

View File

@@ -0,0 +1,48 @@
package com.nbee.echolink.broadcast;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;
import com.nbee.echolink.service.MonitorService;
public class SMSReceiver extends BroadcastReceiver {
private static final String TAG = "SMSReceiver";
private static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";
@Override
public void onReceive(Context context, Intent intent) {
if (SMS_RECEIVED_ACTION.equals(intent.getAction())) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
Object[] pdus = (Object[]) bundle.get("pdus");
if (pdus != null) {
StringBuilder fullMessage = new StringBuilder();
String sender = null;
for (Object pdu : pdus) {
SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu);
if (sender == null) {
// 只需要从第一部分获取发件人号码
sender = smsMessage.getDisplayOriginatingAddress();
}
// 拼接每个部分的消息内容
fullMessage.append(smsMessage.getMessageBody());
}
Log.d(TAG, "Sender: " + sender + ", Message: " + fullMessage.toString());
// 在这里处理完整的短信内容
Intent serviceIntent = new Intent(context, MonitorService.class);
serviceIntent.putExtra("sender", sender);
serviceIntent.putExtra("messageBody", fullMessage.toString());
context.startService(serviceIntent);
}
}
}
}
}

View File

@@ -0,0 +1,40 @@
package com.nbee.echolink.model;
public class CallInfo {
private String phoneNumber;
private String callTime;
public CallInfo() {
}
public CallInfo(String phoneNumber, String callTime) {
this.phoneNumber = phoneNumber;
this.callTime = callTime;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getCallTime() {
return callTime;
}
public void setCallTime(String callTime) {
this.callTime = callTime;
}
@Override
public String toString() {
return "CallInfo{" +
"phoneNumber='" + phoneNumber + '\'' +
", callTime='" + callTime + '\'' +
'}';
}
}

View File

@@ -0,0 +1,52 @@
package com.nbee.echolink.model;
import lombok.Data;
public class SMSInfo {
private String smsNumber;
private String smsAcceptanceTime;
private String smsContent;
public SMSInfo() {
}
public SMSInfo(String smsNumber, String smsAcceptanceTime, String smsContent) {
this.smsNumber = smsNumber;
this.smsAcceptanceTime = smsAcceptanceTime;
this.smsContent = smsContent;
}
public String getSmsNumber() {
return smsNumber;
}
public void setSmsNumber(String smsNumber) {
this.smsNumber = smsNumber;
}
public String getSmsAcceptanceTime() {
return smsAcceptanceTime;
}
public void setSmsAcceptanceTime(String smsAcceptanceTime) {
this.smsAcceptanceTime = smsAcceptanceTime;
}
public String getSmsContent() {
return smsContent;
}
public void setSmsContent(String smsContent) {
this.smsContent = smsContent;
}
@Override
public String toString() {
return "SMSInfo{" +
"smsNumber='" + smsNumber + '\'' +
", smsAcceptanceTime='" + smsAcceptanceTime + '\'' +
", smsContent='" + smsContent + '\'' +
'}';
}
}

View File

@@ -0,0 +1,58 @@
package com.nbee.echolink.response;
import lombok.Data;
public class ApiResponse<T> {
private String code;
/** Response message. */
private String msg;
/** Response timestamp. */
private Long timestamp;
/** Response data. */
private T data;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "ApiResponse{" +
"code='" + code + '\'' +
", msg='" + msg + '\'' +
", timestamp=" + timestamp +
", data=" + data +
'}';
}
}

View File

@@ -0,0 +1,121 @@
package com.nbee.echolink.service;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.nbee.echolink.R;
import com.nbee.echolink.model.CallInfo;
import com.nbee.echolink.model.SMSInfo;
import com.nbee.echolink.utils.NetworkUtil;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MonitorService extends Service {
private NetworkUtil networkUtil;
private static final String TAG = "MonitorService";
private String lastPhoneNumber = null;
private long lastCallTime = 0;
@Override
public void onCreate() {
super.onCreate();
startForegroundService();
networkUtil = new NetworkUtil(this);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
if (intent.hasExtra("incomingNumber")) {
String incomingNumber = intent.getStringExtra("incomingNumber");
Log.i(TAG, "获取到来电信息,号码: "+ incomingNumber);
sendCallInfoToServer(incomingNumber);
} else if (intent.hasExtra("sender") && intent.hasExtra("messageBody")) {
String sender = intent.getStringExtra("sender");
String messageBody = intent.getStringExtra("messageBody");
Log.i(TAG, "获取到短信信息,号码: "+ sender);
sendSmsInfoToServer(sender, messageBody);
}
}
return START_STICKY;
}
private void sendCallInfoToServer(String incomingNumber) {
if (incomingNumber.equals("null") || incomingNumber == null) {
return;
}
long currentTimeMillis = System.currentTimeMillis();
if (incomingNumber.equals(lastPhoneNumber) && (currentTimeMillis - lastCallTime) < 70000) {
// 如果电话号码与上次相同且在70秒之内则不执行请求
Log.i(TAG, "sendCallInfoToServer: 电话号码与上次相同且在70秒之内不执行请求.");
return;
}
// 更新存储的电话号码和时间戳
lastPhoneNumber = incomingNumber;
lastCallTime = currentTimeMillis;
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
CallInfo callInfo = new CallInfo();
callInfo.setCallTime(currentTime);
callInfo.setPhoneNumber(incomingNumber);
networkUtil.postRequest(callInfo);
}
private void sendSmsInfoToServer(String sender,String messageBody){
if (checkNullString(sender,messageBody)){
return;
}
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
SMSInfo smsInfo = new SMSInfo();
smsInfo.setSmsNumber(sender);
smsInfo.setSmsContent(messageBody);
smsInfo.setSmsAcceptanceTime(currentTime);
networkUtil.postRequest(smsInfo);
}
private boolean checkNullString(String a, String b){
if (a == null || b == null){
return true;
}
if (a.equals("null") || b.equals("null")){
return true;
}
return false;
}
private void startForegroundService() {
// 创建通知频道仅在API 26及以上版本中需要
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("channel_id", "Channel Name", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
// 创建通知
Notification notification = new NotificationCompat.Builder(this, "channel_id")
.setContentTitle("服务运行中")
.setContentText("EchoLink正在运行")
.setSmallIcon(R.drawable.ic_notification) // 确保您有这个图标
.build();
// 启动前台服务
startForeground(1, notification);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null; // 不提供绑定服务的接口
}
}

View File

@@ -0,0 +1,95 @@
package com.nbee.echolink.utils;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import com.google.gson.Gson;
import com.nbee.echolink.R;
import com.nbee.echolink.model.CallInfo;
import com.nbee.echolink.model.SMSInfo;
import com.nbee.echolink.response.ApiResponse;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
public class NetworkUtil {
private final String TAG = "NetworkUtil";
private String callApiUrl;
private String smsApiUrl;
private String accessToken;
private Handler handler = new Handler();
private static final int MAX_RETRIES = 3;
private static final long RETRY_DELAY_MS = 2000; // 重试延迟例如2秒
// API URL
public NetworkUtil(Context context) {
callApiUrl = context.getResources().getString(R.string.call_api_url);
smsApiUrl = context.getResources().getString(R.string.message_api_url);
accessToken = context.getResources().getString(R.string.access_token);
}
public <T> void postRequest(T dataObject) {
if (dataObject instanceof CallInfo) {
postRequestWithRetry(dataObject, 0,callApiUrl);
} else if (dataObject instanceof SMSInfo) {
postRequestWithRetry(dataObject, 0,smsApiUrl);
}
}
private <T> void postRequestWithRetry(T dataObject, int retryCount,String apiUrl) {
OkHttpClient client = new OkHttpClient();
// ... 构建请求 ...
Request request = buildRequest(dataObject,apiUrl);
client.newCall(request).enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
if (retryCount < MAX_RETRIES) {
handler.postDelayed(() -> postRequestWithRetry(dataObject, retryCount + 1,apiUrl), RETRY_DELAY_MS);
} else {
Log.e(TAG, "onFailure: Failed after " + MAX_RETRIES + " attempts", e);
// 超出重试次数,处理失败情况
}
}
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
if (!response.isSuccessful()) {
// 在这里处理响应错误
Log.d("onResponse", response.message());
throw new IOException("Unexpected code " + response);
}
// ... 处理响应 ...
Gson gson1 = new Gson();
ApiResponse apiResponse = gson1.fromJson(response.body().string(), ApiResponse.class);
if (apiResponse.getCode().equals(0)){
Log.i(TAG, "onResponse ResponseCode: " + apiResponse.getCode() + "ResponseMessage" + apiResponse.getMsg());
}
}
});
}
private <T>Request buildRequest(T dataObject,String apiUrl){
Gson gson = new Gson();
// 将对象转换为JSON字符串
String json = gson.toJson(dataObject);
// 创建请求体
MediaType JSON = MediaType.get("application/json; charset=utf-8");
RequestBody body = RequestBody.create(json, JSON);
// 构建请求,并设置请求头
Request request = new Request.Builder()
.url(apiUrl)
.addHeader("accessToken", accessToken) // 使用加载的accessToken
.addHeader("Content-Type", "application/json")
.post(body)
.build();
return request;
}
}