新增:监听其他APP通知信息

This commit is contained in:
pppscn 2021-11-13 18:01:50 +08:00
parent 27d35cb276
commit e8ea004bc4
20 changed files with 2104 additions and 1612 deletions

View File

@ -260,6 +260,7 @@
+ https://github.com/xuexiangjys/XUpdateAPI (在线升级) + https://github.com/xuexiangjys/XUpdateAPI (在线升级)
+ https://github.com/mailhu/emailkit (邮件发送) + https://github.com/mailhu/emailkit (邮件发送)
+ https://github.com/alibaba/fastjson (Json解析) + https://github.com/alibaba/fastjson (Json解析)
+ https://github.com/lilongweidev/NotifyListenerDemo (手机通知服务)
## LICENSE ## LICENSE

View File

@ -22,7 +22,7 @@ android {
} }
defaultConfig { defaultConfig {
applicationId "com.idormy.sms.forwarder" applicationId "com.idormy.sms.forwarder"
minSdkVersion 23 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 30
versionCode versionProps['versionCode'].toInteger() versionCode versionProps['versionCode'].toInteger()
versionName versionProps['versionName'] versionName versionProps['versionName']
@ -117,7 +117,7 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

View File

@ -29,18 +29,26 @@
<uses-permission <uses-permission
android:name="android.permission.BATTERY_STATS" android:name="android.permission.BATTERY_STATS"
tools:ignore="ProtectedPermissions" /> tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.ACTION_NOTIFICATION_LISTENER_SETTINGS" />
<uses-permission
android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" />
<application <application
android:name=".MyApplication" android:name=".MyApplication"
android:allowBackup="true" android:allowBackup="true"
android:fullBackupContent="@xml/backup_descriptor"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true">
android:fullBackupContent="@xml/backup_descriptor">
<meta-data <meta-data
android:name="UPDATE_APP_KEY" android:name="UPDATE_APP_KEY"
@ -67,8 +75,8 @@
<intent-filter> <intent-filter>
<!--协议部分,随便设置--> <!--协议部分,随便设置-->
<data <data
android:scheme="forwarder" android:host="main"
android:host="main" /> android:scheme="forwarder" />
<!--下面这几行也必须得设置--> <!--下面这几行也必须得设置-->
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
@ -92,8 +100,9 @@
android:name=".receiver.RebootBroadcastReceiver" android:name=".receiver.RebootBroadcastReceiver"
tools:ignore="IntentFilterExportedReceiver"> tools:ignore="IntentFilterExportedReceiver">
<intent-filter android:priority="2147483647"> <intent-filter android:priority="2147483647">
<!--重启广播-->
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
<action android:name="android.intent.action.PACKAGE_RESTARTED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver <receiver
@ -105,7 +114,6 @@
<action android:name="android.provider.Telephony.SMS_RECEIVED" /> <action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver <receiver
android:name=".receiver.PhoneStateReceiver" android:name=".receiver.PhoneStateReceiver"
tools:ignore="IntentFilterExportedReceiver"> tools:ignore="IntentFilterExportedReceiver">
@ -114,7 +122,22 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<service android:name=".service.FrontService" /> <service
android:name=".service.FrontService"
android:enabled="true" />
<service
android:name=".service.BatteryService"
android:enabled="true" />
<service
android:name=".service.NotifyService"
android:enabled="true"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
</application> </application>
</manifest> </manifest>

View File

@ -1,10 +1,15 @@
package com.idormy.sms.forwarder; package com.idormy.sms.forwarder;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.PowerManager;
import android.service.notification.StatusBarNotification;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -13,27 +18,43 @@ import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationManagerCompat;
import com.idormy.sms.forwarder.adapter.LogAdapter; import com.idormy.sms.forwarder.adapter.LogAdapter;
import com.idormy.sms.forwarder.model.vo.LogVo; import com.idormy.sms.forwarder.model.vo.LogVo;
import com.idormy.sms.forwarder.model.vo.SmsVo;
import com.idormy.sms.forwarder.notify.NotifyHelper;
import com.idormy.sms.forwarder.notify.NotifyListener;
import com.idormy.sms.forwarder.sender.SendUtil;
import com.idormy.sms.forwarder.service.FrontService;
import com.idormy.sms.forwarder.service.NotifyService;
import com.idormy.sms.forwarder.utils.LogUtil; import com.idormy.sms.forwarder.utils.LogUtil;
import com.idormy.sms.forwarder.utils.NetUtil; import com.idormy.sms.forwarder.utils.NetUtil;
import com.idormy.sms.forwarder.utils.PhoneUtils; import com.idormy.sms.forwarder.utils.PhoneUtils;
import com.idormy.sms.forwarder.utils.SmsUtil; import com.idormy.sms.forwarder.utils.SmsUtil;
import com.idormy.sms.forwarder.utils.aUtil; import com.idormy.sms.forwarder.utils.aUtil;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Set;
public class MainActivity extends AppCompatActivity implements RefreshListView.IRefreshListener { public class MainActivity extends AppCompatActivity implements NotifyListener, RefreshListView.IRefreshListener {
private final String TAG = "MainActivity"; private final String TAG = "MainActivity";
private static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
// logVoList用于存储数据 // logVoList用于存储数据
private List<LogVo> logVos = new ArrayList<>(); private List<LogVo> logVos = new ArrayList<>();
private LogAdapter adapter; private LogAdapter adapter;
private RefreshListView listView; private RefreshListView listView;
private Intent serviceIntent;
private static final int REQUEST_CODE = 9999;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -54,6 +75,14 @@ public class MainActivity extends AppCompatActivity implements RefreshListView.I
//短信&网络组件初始化 //短信&网络组件初始化
SmsUtil.init(this); SmsUtil.init(this);
NetUtil.init(this); NetUtil.init(this);
//应用通知
NotifyHelper.getInstance().setNotifyListener(this);
//前台服务
serviceIntent = new Intent(MainActivity.this, FrontService.class);
serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startService(serviceIntent);
} }
@Override @Override
@ -105,6 +134,7 @@ public class MainActivity extends AppCompatActivity implements RefreshListView.I
}); });
} }
@SuppressLint("ObsoleteSdkInt")
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
@ -113,8 +143,98 @@ public class MainActivity extends AppCompatActivity implements RefreshListView.I
MyApplication.SimInfoList = PhoneUtils.getSimMultiInfo(); MyApplication.SimInfoList = PhoneUtils.getSimMultiInfo();
} }
Log.d(TAG, "SimInfoList = " + MyApplication.SimInfoList.size()); Log.d(TAG, "SimInfoList = " + MyApplication.SimInfoList.size());
//开启读取通知栏权限
if (!isNotificationListenerServiceEnabled(this)) {
openNotificationAccess();
toggleNotificationListenerService();
Toast.makeText(this, "请先勾选《短信转发器》的读取通知栏权限!", Toast.LENGTH_LONG).show();
return;
} }
//省电优化设置为无限制
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
if (!isIgnoringBatteryOptimizations()) {
Toast.makeText(this, "请将省电优化设置为无限制,有利于防止《短信转发器》被杀!!", Toast.LENGTH_LONG).show();
}
}
startService(serviceIntent);
}
@Override
protected void onDestroy() {
super.onDestroy();
startService(serviceIntent);
}
@Override
protected void onPause() {
//当界面返回到桌面之后.清除通知设置当中的数据.减少内存占有
//if (applicationList != null) {
// applicationList.setAdapter(null);
// adapter = null;
//}
super.onPause();
startService(serviceIntent);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE) {
if (isNotificationListenerServiceEnabled(this)) {
Toast.makeText(this, "通知服务已开启", Toast.LENGTH_SHORT).show();
toggleNotificationListenerService();
} else {
Toast.makeText(this, "通知服务未开启", Toast.LENGTH_SHORT).show();
}
}
}
// 权限判断相关
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
public void toggleNotificationListenerService() {
PackageManager pm = getPackageManager();
pm.setComponentEnabledSetting(new ComponentName(getApplicationContext(), NotifyService.class),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(new ComponentName(getApplicationContext(), NotifyService.class),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
private static boolean isNotificationListenerServiceEnabled(Context context) {
Set<String> packageNames = NotificationManagerCompat.getEnabledListenerPackages(context);
return packageNames.contains(context.getPackageName());
}
/**
* 判断系统是否已经关闭省电优化
*
* @return boolean
*/
@RequiresApi(api = Build.VERSION_CODES.M)
private boolean isIgnoringBatteryOptimizations() {
boolean isIgnoring = false;
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (powerManager != null) {
isIgnoring = powerManager.isIgnoringBatteryOptimizations(getPackageName());
}
if (!isIgnoring) {
Intent i = new Intent(android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
startActivity(i);
}
return isIgnoring;
}
private void openNotificationAccess() {
startActivity(new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS));
}
// 初始化数据 // 初始化数据
private void initTLogs() { private void initTLogs() {
logVos = LogUtil.getLog(null, null); logVos = LogUtil.getLog(null, null);
@ -148,7 +268,6 @@ public class MainActivity extends AppCompatActivity implements RefreshListView.I
}, 2000); }, 2000);
} }
public void logDetail(LogVo logVo) { public void logDetail(LogVo logVo) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle(R.string.details); builder.setTitle(R.string.details);
@ -241,7 +360,71 @@ public class MainActivity extends AppCompatActivity implements RefreshListView.I
MenuInflater inflater = getMenuInflater(); MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_main, menu); inflater.inflate(R.menu.menu_main, menu);
return true; return true;
}
/**
* 收到通知
*
* @param type 通知类型
*/
@Override
public void onReceiveMessage(int type) {
Log.d(TAG, "收到通知=" + type);
}
/**
* 移除通知
*
* @param type 通知类型
*/
@Override
public void onRemovedMessage(int type) {
Log.d(TAG, "移除通知=" + type);
}
/**
* 收到通知
*
* @param sbn 状态栏通知
*/
@Override
public void onReceiveMessage(StatusBarNotification sbn) {
if (sbn.getNotification() == null) return;
//推送通知的应用包名
String packageName = sbn.getPackageName();
//通知标题
String title = sbn.getNotification().extras.get("android.title").toString();
//通知内容
String text = sbn.getNotification().extras.get("android.text").toString();
if (text.isEmpty() && sbn.getNotification().tickerText != null) {
text = sbn.getNotification().tickerText.toString();
}
//通知时间
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINESE).format(new Date(sbn.getPostTime()));
Log.d(TAG, String.format(
Locale.getDefault(),
"onNotificationPosted\n应用包名%s\n消息标题%s\n消息内容%s\n消息时间%s\n",
packageName, title, text, time)
);
//不处理空消息
if (title.isEmpty() && text.isEmpty()) return;
SmsVo smsVo = new SmsVo(packageName, text, new Date(), title);
Log.d(TAG, "send_msg" + smsVo.toString());
SendUtil.send_msg(this, smsVo, 1);
}
/**
* 移除掉通知栏消息
*
* @param sbn 状态栏通知
*/
@Override
public void onRemovedMessage(StatusBarNotification sbn) {
Log.d(TAG, "移除掉通知栏消息");
} }
} }

View File

@ -1,6 +1,9 @@
package com.idormy.sms.forwarder; package com.idormy.sms.forwarder;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
@ -9,17 +12,24 @@ import android.view.View;
import android.widget.EditText; import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.Switch; import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationManagerCompat;
import com.idormy.sms.forwarder.service.NotifyService;
import com.idormy.sms.forwarder.utils.KeepAliveUtils; import com.idormy.sms.forwarder.utils.KeepAliveUtils;
import com.idormy.sms.forwarder.utils.SettingUtil; import com.idormy.sms.forwarder.utils.SettingUtil;
import java.util.Set;
public class SettingActivity extends AppCompatActivity { public class SettingActivity extends AppCompatActivity {
private final String TAG = "SettingActivity"; private final String TAG = "SettingActivity";
private static final int REQUEST_CODE = 9527;
private TextView textView;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate"); Log.d(TAG, "onCreate");
@ -285,4 +295,62 @@ public class SettingActivity extends AppCompatActivity {
KeepAliveUtils.ignoreBatteryOptimization(this); KeepAliveUtils.ignoreBatteryOptimization(this);
} }
} }
/**
* 请求权限
*
* @param view 控件
*/
public void requestPermission(View view) {
if (!isNLServiceEnabled()) {
Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
startActivityForResult(intent, REQUEST_CODE);
} else {
showMsg("通知服务已开启");
toggleNotificationListenerService();
}
}
/**
* 是否启用通知监听服务
*
* @return boolean
*/
public boolean isNLServiceEnabled() {
Set<String> packageNames = NotificationManagerCompat.getEnabledListenerPackages(this);
return packageNames.contains(getPackageName());
}
/**
* 切换通知监听器服务
*/
public void toggleNotificationListenerService() {
PackageManager pm = getPackageManager();
pm.setComponentEnabledSetting(new ComponentName(getApplicationContext(), NotifyService.class),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(new ComponentName(getApplicationContext(), NotifyService.class),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE) {
if (isNLServiceEnabled()) {
showMsg("通知服务已开启");
toggleNotificationListenerService();
} else {
showMsg("通知服务未开启");
}
}
}
private void showMsg(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
} }

View File

@ -0,0 +1,76 @@
package com.idormy.sms.forwarder.notify;
import android.service.notification.StatusBarNotification;
public class NotifyHelper {
private static NotifyHelper instance;
public static final int N_MESSAGE = 0;
public static final int N_CALL = 1;
public static final int N_QQ = 2;
public static final int N_WX = 3;
private NotifyListener notifyListener;
public static NotifyHelper getInstance() {
if (instance == null) {
instance = new NotifyHelper();
}
return instance;
}
/**
* 收到消息
*
* @param type 消息类型
*/
public void onReceive(int type) {
if (notifyListener != null) {
notifyListener.onReceiveMessage(type);
}
}
/**
* 收到消息
*
* @param sbn 状态栏通知
*/
public void onReceive(StatusBarNotification sbn) {
if (notifyListener != null) {
notifyListener.onReceiveMessage(sbn);
}
}
/**
* 移除消息
*
* @param type 消息类型
*/
public void onRemoved(int type) {
if (notifyListener != null) {
notifyListener.onRemovedMessage(type);
}
}
/**
* 移除消息
*
* @param sbn 状态栏通知
*/
public void onRemoved(StatusBarNotification sbn) {
if (notifyListener != null) {
notifyListener.onRemovedMessage(sbn);
}
}
/**
* 设置回调方法
*
* @param notifyListener 通知监听
*/
public void setNotifyListener(NotifyListener notifyListener) {
this.notifyListener = notifyListener;
}
}

View File

@ -0,0 +1,34 @@
package com.idormy.sms.forwarder.notify;
import android.service.notification.StatusBarNotification;
public interface NotifyListener {
/**
* 接收到通知栏消息
*
* @param type
*/
void onReceiveMessage(int type);
/**
* 移除掉通知栏消息
*
* @param type
*/
void onRemovedMessage(int type);
/**
* 接收到通知栏消息
*
* @param sbn
*/
void onReceiveMessage(StatusBarNotification sbn);
/**
* 移除掉通知栏消息
*
* @param sbn
*/
void onRemovedMessage(StatusBarNotification sbn);
}

View File

@ -76,7 +76,7 @@ public class SendUtil {
public static void sendMsgByRuleModelSenderId(final Handler handError, RuleModel ruleModel, SmsVo smsVo, Long senderId) throws Exception { public static void sendMsgByRuleModelSenderId(final Handler handError, RuleModel ruleModel, SmsVo smsVo, Long senderId) throws Exception {
if (senderId == null) { if (senderId == null) {
throw new Exception("先新建并选择发送"); throw new Exception("先新建并选择发送通道");
} }
String testSim = smsVo.getSimInfo().substring(0, 4); String testSim = smsVo.getSimInfo().substring(0, 4);
@ -92,7 +92,7 @@ public class SendUtil {
List<SenderModel> senderModels = SenderUtil.getSender(senderId, null); List<SenderModel> senderModels = SenderUtil.getSender(senderId, null);
if (senderModels.isEmpty()) { if (senderModels.isEmpty()) {
throw new Exception("未找到发送"); throw new Exception("未找到发送通道");
} }
for (SenderModel senderModel : senderModels for (SenderModel senderModel : senderModels

View File

@ -47,7 +47,6 @@ public class BatteryService extends Service {
Log.i(TAG, "onDestroy--------------"); Log.i(TAG, "onDestroy--------------");
super.onDestroy(); super.onDestroy();
this.unregisterReceiver(batteryReceiver); this.unregisterReceiver(batteryReceiver);
} }
// 接收电池信息更新的广播 // 接收电池信息更新的广播
@ -105,7 +104,6 @@ public class BatteryService extends Service {
Log.i(TAG, "battery: date=" + date + ",status " + statusString Log.i(TAG, "battery: date=" + date + ",status " + statusString
+ ",level=" + level + ",scale=" + scale + ",level=" + level + ",scale=" + scale
+ ",voltage=" + voltage + ",acString=" + acString); + ",voltage=" + voltage + ",acString=" + acString);
} }
}; };
} }

View File

@ -6,16 +6,19 @@ import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.ContextWrapper; import android.content.ContextWrapper;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.BatteryManager; import android.os.BatteryManager;
import android.os.Build; import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.NotificationManagerCompat;
import com.idormy.sms.forwarder.MainActivity; import com.idormy.sms.forwarder.MainActivity;
import com.idormy.sms.forwarder.MyApplication; import com.idormy.sms.forwarder.MyApplication;
@ -27,14 +30,15 @@ import com.idormy.sms.forwarder.utils.PhoneUtils;
import com.idormy.sms.forwarder.utils.SettingUtil; import com.idormy.sms.forwarder.utils.SettingUtil;
import java.util.Date; import java.util.Date;
import java.util.Set;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
public class FrontService extends Service { public class FrontService extends Service {
private static final String TAG = "FrontService"; private static final String TAG = "FrontService";
private static final String CHANNEL_ONE_ID = "com.idormy.sms.forwarder"; private static final String CHANNEL_ONE_ID = "com.idormy.sms.forwarder";
private static final String CHANNEL_ONE_NAME = "com.idormy.sms.forwarderName"; private static final String CHANNEL_ONE_NAME = "com.idormy.sms.forwarderName";
private static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
@SuppressLint("IconColors") @SuppressLint("IconColors")
@Override @Override
@ -43,15 +47,10 @@ public class FrontService extends Service {
Log.i(TAG, "onCreate"); Log.i(TAG, "onCreate");
Notification.Builder builder = new Notification.Builder(this); Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.drawable.ic_forwarder); builder.setSmallIcon(R.drawable.ic_forwarder);
//OSUtils.ROM_TYPE romType = OSUtils.getRomType();
//Log.d(TAG, "onCreate: " + romType);
//if (romType == OSUtils.ROM_TYPE.MIUI_ROM) {
builder.setContentTitle(getString(R.string.app_name)); builder.setContentTitle(getString(R.string.app_name));
//}
builder.setContentText(getString(R.string.notification_content)); builder.setContentText(getString(R.string.notification_content));
Intent intent = new Intent(this, MainActivity.class); Intent intent = new Intent(this, MainActivity.class);
@SuppressLint("UnspecifiedImmutableFlag") PendingIntent pendingIntent = PendingIntent.getActivity @SuppressLint("UnspecifiedImmutableFlag") PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent); builder.setContentIntent(pendingIntent);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
@ -112,6 +111,27 @@ public class FrontService extends Service {
} }
} }
}, 0, 10000); }, 0, 10000);
if (!isNotificationListenerServiceEnabled(this)) {
openNotificationAccess();
}
toggleNotificationListenerService();
}
@Override
public void onDestroy() {
//进行自动重启
Intent intent = new Intent(FrontService.this, FrontService.class);
//重新开启服务
startService(intent);
stopForeground(true);
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//return super.onStartCommand(intent, flags, startId);
return START_STICKY; //保证service不被杀死
} }
@Nullable @Nullable
@ -120,12 +140,6 @@ public class FrontService extends Service {
return null; return null;
} }
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "flags: " + flags + " startId: " + startId);
return START_STICKY; //保证service不被杀死
}
//获取当前电量 //获取当前电量
@SuppressLint("ObsoleteSdkInt") @SuppressLint("ObsoleteSdkInt")
private int getBatteryLevel() { private int getBatteryLevel() {
@ -139,4 +153,22 @@ public class FrontService extends Service {
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
} }
} }
private void toggleNotificationListenerService() {
PackageManager pm = getPackageManager();
pm.setComponentEnabledSetting(new ComponentName(this, NotifyService.class),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(new ComponentName(this, NotifyService.class),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
private static boolean isNotificationListenerServiceEnabled(Context context) {
Set<String> packageNames = NotificationManagerCompat.getEnabledListenerPackages(context);
return packageNames.contains(context.getPackageName());
}
private void openNotificationAccess() {
startActivity(new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS));
}
} }

View File

@ -0,0 +1,52 @@
package com.idormy.sms.forwarder.service;
import android.content.ComponentName;
import android.os.Build;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.idormy.sms.forwarder.notify.NotifyHelper;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public class NotifyService extends NotificationListenerService {
public static final String TAG = "NotifyService";
/**
* 发布通知
*
* @param sbn 状态栏通知
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
if (sbn.getNotification() == null) return;
Log.d(TAG, sbn.getPackageName());
NotifyHelper.getInstance().onReceive(sbn);
}
/**
* 通知已删除
*
* @param sbn 状态栏通知
*/
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
Log.d(TAG, sbn.getPackageName());
NotifyHelper.getInstance().onRemoved(sbn);
}
/**
* 监听断开
*/
@Override
public void onListenerDisconnected() {
Log.d(TAG, "通知侦听器断开连接 - 请求重新绑定");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
requestRebind(new ComponentName(this, NotificationListenerService.class));
}
}
}

View File

@ -545,12 +545,13 @@ public class PhoneUtils {
boolean permission_read_call_log = (PackageManager.PERMISSION_GRANTED == pm.checkPermission("android.permission.READ_CALL_LOG", that.getPackageName())); boolean permission_read_call_log = (PackageManager.PERMISSION_GRANTED == pm.checkPermission("android.permission.READ_CALL_LOG", that.getPackageName()));
boolean permission_read_contacts = (PackageManager.PERMISSION_GRANTED == pm.checkPermission("android.permission.READ_CONTACTS", that.getPackageName())); boolean permission_read_contacts = (PackageManager.PERMISSION_GRANTED == pm.checkPermission("android.permission.READ_CONTACTS", that.getPackageName()));
boolean permission_battery_stats = (PackageManager.PERMISSION_GRANTED == pm.checkPermission("android.permission.BATTERY_STATS", that.getPackageName())); boolean permission_battery_stats = (PackageManager.PERMISSION_GRANTED == pm.checkPermission("android.permission.BATTERY_STATS", that.getPackageName()));
boolean permission_bind_notification_listener_service = (PackageManager.PERMISSION_GRANTED == pm.checkPermission("android.permission.BIND_NOTIFICATION_LISTENER_SERVICE", that.getPackageName()));
if (!(permission_internet && permission_receive_boot && permission_foreground_service && if (!(permission_internet && permission_receive_boot && permission_foreground_service &&
permission_read_external_storage && permission_write_external_storage && permission_read_external_storage && permission_write_external_storage &&
permission_receive_sms && permission_read_sms && permission_send_sms && permission_receive_sms && permission_read_sms && permission_send_sms &&
permission_read_call_log && permission_read_contacts && permission_read_call_log && permission_read_contacts &&
permission_read_phone_state && permission_read_phone_numbers && permission_battery_stats)) { permission_read_phone_state && permission_read_phone_numbers && permission_battery_stats && permission_bind_notification_listener_service)) {
ActivityCompat.requestPermissions((Activity) that, new String[]{ ActivityCompat.requestPermissions((Activity) that, new String[]{
Manifest.permission.INTERNET, Manifest.permission.INTERNET,
Manifest.permission.RECEIVE_BOOT_COMPLETED, Manifest.permission.RECEIVE_BOOT_COMPLETED,
@ -565,6 +566,7 @@ public class PhoneUtils {
Manifest.permission.READ_PHONE_NUMBERS, Manifest.permission.READ_PHONE_NUMBERS,
Manifest.permission.FOREGROUND_SERVICE, Manifest.permission.FOREGROUND_SERVICE,
Manifest.permission.BATTERY_STATS, Manifest.permission.BATTERY_STATS,
Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE,
}, 0x01); }, 0x01);
} }
} }

View File

@ -17,7 +17,6 @@ public class SmsUtil {
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
static Context context; static Context context;
public static void init(Context context1) { public static void init(Context context1) {
synchronized (hasInit) { synchronized (hasInit) {
if (hasInit) return; if (hasInit) return;

View File

@ -436,23 +436,44 @@
</LinearLayout> </LinearLayout>
<Button <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="35dp" android:layout_height="wrap_content"
android:layout_margin="10dp" android:layout_marginTop="2dp"
android:background="@color/colorPrimary" android:background="@android:color/white"
android:onClick="initSetting" android:gravity="center_vertical"
android:padding="0dp" android:orientation="horizontal"
android:text="@string/init_setting" /> android:padding="15dp">
<Button <Button
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="35dp" android:layout_height="35dp"
android:layout_margin="10dp" android:layout_weight="1"
android:background="@color/colorPrimary" android:background="@color/colorPrimary"
android:onClick="batterySetting" android:onClick="batterySetting"
android:padding="0dp" android:text="@string/battery_setting"
android:text="@string/battery_setting" /> tools:ignore="ButtonStyle,NestedWeights" />
<Button
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:background="@color/colorPrimary"
android:onClick="requestPermission"
android:text="@string/request_permission"
tools:ignore="ButtonStyle,NestedWeights" />
<Button
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:background="@color/colorBlueGreyDark"
android:onClick="initSetting"
android:text="@string/init_setting"
tools:ignore="ButtonStyle,NestedWeights" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -159,6 +159,7 @@
<string name="insert_device_name">Device</string> <string name="insert_device_name">Device</string>
<string name="init_setting">Restore initial Setting</string> <string name="init_setting">Restore initial Setting</string>
<string name="battery_setting">Battery Optimization</string> <string name="battery_setting">Battery Optimization</string>
<string name="request_permission">Request Notify Permission</string>
<string name="unknown_number">Unknown Number</string> <string name="unknown_number">Unknown Number</string>
<string name="calling">Incoming telegram</string> <string name="calling">Incoming telegram</string>
<string name="unsupport">Your phone does not support this setting</string> <string name="unsupport">Your phone does not support this setting</string>

View File

@ -12,11 +12,11 @@
<string name="clone">一键克隆</string> <string name="clone">一键克隆</string>
<string name="setting">设置</string> <string name="setting">设置</string>
<string name="about">关于</string> <string name="about">关于</string>
<string name="rule_setting">设置转发规则</string> <string name="rule_setting">转发规则</string>
<string name="sender_setting">设置发送方</string> <string name="sender_setting">发送通道</string>
<string name="log_tips">提示:置顶下拉刷新,长按删除单条记录</string> <string name="log_tips">提示:置顶下拉刷新,长按删除单条记录</string>
<string name="rule_tips">提示:新建规则点击“添加”,长按删除/点击编辑已有</string> <string name="rule_tips">提示:新建规则点击“添加”,长按删除/点击编辑已有</string>
<string name="sender_tips">提示:新建发送点击“添加”,长按删除/点击编辑已有</string> <string name="sender_tips">提示:新建发送通道点击“添加”,长按删除/点击编辑已有</string>
<!--AboutActivity--> <!--AboutActivity-->
<string name="version">当前版本</string> <string name="version">当前版本</string>
<string name="check_for_updates">检查新版本</string> <string name="check_for_updates">检查新版本</string>
@ -50,9 +50,9 @@
<string name="delete_rule_title">规则删除确认</string> <string name="delete_rule_title">规则删除确认</string>
<string name="delete_rule_tips">确定删除该条规则?</string> <string name="delete_rule_tips">确定删除该条规则?</string>
<string name="delete_rule_toast">该条规则已经删除!</string> <string name="delete_rule_toast">该条规则已经删除!</string>
<string name="new_sender_first">请先创建发送再选择</string> <string name="new_sender_first">请先创建发送通道再选择</string>
<string name="add_sender_first">请先去设置发送页面添加</string> <string name="add_sender_first">请先去设置发送通道页面添加</string>
<string name="select_sender">选择发送</string> <string name="select_sender">选择发送通道</string>
<string name="rule_tester">规则测试</string> <string name="rule_tester">规则测试</string>
<string name="new_forwarding_rule">添加转发规则</string> <string name="new_forwarding_rule">添加转发规则</string>
<string name="test_sim_slot">测试模拟的接收卡槽</string> <string name="test_sim_slot">测试模拟的接收卡槽</string>
@ -72,11 +72,11 @@
<string name="btn_regex">正则匹配</string> <string name="btn_regex">正则匹配</string>
<string name="match_value">设置匹配的值</string> <string name="match_value">设置匹配的值</string>
<!--SenderActivity--> <!--SenderActivity-->
<string name="invalid_sender">异常的发送类型,自动删除!</string> <string name="invalid_sender">异常的发送通道类型,自动删除!</string>
<string name="delete_sender_title">发送删除确认</string> <string name="delete_sender_title">发送通道删除确认</string>
<string name="delete_sender_tips">确定删除该条发送?</string> <string name="delete_sender_tips">确定删除该条发送通道?</string>
<string name="delete_sender_toast">该条发送已经删除!</string> <string name="delete_sender_toast">该条发送通道已经删除!</string>
<string name="add_sender_title">选择发送类型</string> <string name="add_sender_title">选择发送通道类型</string>
<string name="not_supported">暂不支持这种转发</string> <string name="not_supported">暂不支持这种转发</string>
<string name="setdingdingtitle">设置钉钉机器人</string> <string name="setdingdingtitle">设置钉钉机器人</string>
<string name="setemailtitle">设置邮箱</string> <string name="setemailtitle">设置邮箱</string>
@ -102,7 +102,7 @@
<string name="invalid_webhook">webHook 不能为空</string> <string name="invalid_webhook">webHook 不能为空</string>
<string name="invalid_at_mobiles">指定成员 不能为空 或者 选择@all</string> <string name="invalid_at_mobiles">指定成员 不能为空 或者 选择@all</string>
<string name="invalid_phone_num">接收手机号不能为空</string> <string name="invalid_phone_num">接收手机号不能为空</string>
<string name="new_sender">添加发送</string> <string name="new_sender">添加发送通道</string>
<string name="set_bark_name">设置分组名称</string> <string name="set_bark_name">设置分组名称</string>
<string name="set_bark_server">设置Bark-Server地址示例https://bark.bms.ink/XXXXXXXX/</string> <string name="set_bark_server">设置Bark-Server地址示例https://bark.bms.ink/XXXXXXXX/</string>
<string name="set_bark_icon">设置推送消息图标(可选)示例http://day.app/assets/images/avatar.jpg</string> <string name="set_bark_icon">设置推送消息图标(可选)示例http://day.app/assets/images/avatar.jpg</string>
@ -157,8 +157,9 @@
<string name="insert_extra">卡槽信息</string> <string name="insert_extra">卡槽信息</string>
<string name="insert_time">接收时间</string> <string name="insert_time">接收时间</string>
<string name="insert_device_name">设备名称</string> <string name="insert_device_name">设备名称</string>
<string name="init_setting">恢复初始化配置</string> <string name="init_setting">恢复默认配置</string>
<string name="battery_setting">设置电池优化</string> <string name="battery_setting">设置电池优化</string>
<string name="request_permission">请求通知权限</string>
<string name="unknown_number">未知号码</string> <string name="unknown_number">未知号码</string>
<string name="calling">来电</string> <string name="calling">来电</string>
<string name="unsupport">您的手机不支持此设置</string> <string name="unsupport">您的手机不支持此设置</string>
@ -174,7 +175,7 @@
<string name="post">POST</string> <string name="post">POST</string>
<string name="get">GET</string> <string name="get">GET</string>
<string name="local_ip">本机IP</string> <string name="local_ip">本机IP</string>
<string name="operating_instruction">操作说明:\n1.请保持新旧手机在同一个WiFi网络下且没有开启隔离\n2.旧手机直接点【发送】按钮获取到【服务端IP】\n3.新手机填写【服务端IP】后点【接收】按钮\n【注意】新手机接收后发送、转发规则将完全被覆盖,清空历史记录!</string> <string name="operating_instruction">操作说明:\n1.新旧手机连接同一个WiFi网络(禁用AP隔离)\n2.旧手机直接点【发送】按钮获取到【服务端IP】\n3.新手机填写【服务端IP】后点【接收】按钮\n【注意】新手机接收后发送通道、转发规则将完全被覆盖,清空历史记录!</string>
<string name="send">发送</string> <string name="send">发送</string>
<string name="stop">停止</string> <string name="stop">停止</string>
<string name="old_mobile_phone">我是旧手机</string> <string name="old_mobile_phone">我是旧手机</string>

View File

@ -8,7 +8,7 @@
# The setting is particularly useful for tweaking memory settings. # The setting is particularly useful for tweaking memory settings.
android.enableJetifier=true android.enableJetifier=true
android.useAndroidX=true android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536m org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode. # When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit # This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects

View File

@ -1 +1,2 @@
rootProject.name = "SmsForwarder"
include ':app' include ':app'

View File

@ -1,3 +1,3 @@
#Fri Jul 16 10:33:23 CST 2021 #Fri Jul 16 10:33:23 CST 2021
versionName=2.1.1 versionName=2.2.0
versionCode=29 versionCode=30