lib补充文件提交
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,39 @@
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk 33
|
||||
|
||||
defaultConfig {
|
||||
minSdk 19
|
||||
targetSdk 32
|
||||
|
||||
consumerProguardFiles 'consumer-rules.pro'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
lint {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation 'androidx.annotation:annotation:1.6.0'
|
||||
implementation 'androidx.core:core:1.10.1'
|
||||
}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,26 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.fanjun.keeplive">
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.GET_TASKS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.REORDER_TASKS" />
|
||||
|
||||
<application>
|
||||
<receiver android:name="com.fanjun.keeplive.receiver.NotificationClickReceiver" />
|
||||
<activity
|
||||
android:name="com.fanjun.keeplive.activity.OnePixelActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@style/onePixelActivity" />
|
||||
|
||||
<service android:name="com.fanjun.keeplive.service.LocalService" />
|
||||
<service android:name="com.fanjun.keeplive.service.HideForegroundService" />
|
||||
<service
|
||||
android:name="com.fanjun.keeplive.service.JobHandlerService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
<service
|
||||
android:name="com.fanjun.keeplive.service.RemoteService"
|
||||
android:process=":remote" />
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.fanjun.keeplive.service;
|
||||
|
||||
interface GuardAidl {
|
||||
//相互唤醒服务
|
||||
void wakeUp(String title, String discription, int iconRes);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.fanjun.keeplive;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.fanjun.keeplive.config.ForegroundNotification;
|
||||
import com.fanjun.keeplive.config.KeepLiveService;
|
||||
import com.fanjun.keeplive.service.JobHandlerService;
|
||||
import com.fanjun.keeplive.service.LocalService;
|
||||
import com.fanjun.keeplive.service.RemoteService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 保活工具
|
||||
*/
|
||||
public final class KeepLive {
|
||||
/**
|
||||
* 运行模式
|
||||
*/
|
||||
public static enum RunMode {
|
||||
/**
|
||||
* 省电模式
|
||||
* 省电一些,但保活效果会差一点
|
||||
*/
|
||||
ENERGY,
|
||||
/**
|
||||
* 流氓模式
|
||||
* 相对耗电,但可造就不死之身
|
||||
*/
|
||||
ROGUE
|
||||
}
|
||||
|
||||
public static ForegroundNotification foregroundNotification = null;
|
||||
public static KeepLiveService keepLiveService = null;
|
||||
public static RunMode runMode = null;
|
||||
public static boolean useSilenceMusice = true;
|
||||
|
||||
/**
|
||||
* 启动保活
|
||||
*
|
||||
* @param application your application
|
||||
* @param foregroundNotification 前台服务 必须要,安卓8.0后必须有前台通知才能正常启动Service
|
||||
* @param keepLiveService 保活业务
|
||||
*/
|
||||
public static void startWork(@NonNull Application application, @NonNull RunMode runMode, @NonNull ForegroundNotification foregroundNotification, @NonNull KeepLiveService keepLiveService) {
|
||||
if (isMain(application)) {
|
||||
KeepLive.foregroundNotification = foregroundNotification;
|
||||
KeepLive.keepLiveService = keepLiveService;
|
||||
KeepLive.runMode = runMode;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
//启动定时器,在定时器中启动本地服务和守护进程
|
||||
Intent intent = new Intent(application, JobHandlerService.class);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
application.startForegroundService(intent);
|
||||
} else {
|
||||
application.startService(intent);
|
||||
}
|
||||
} else {
|
||||
//启动本地服务
|
||||
Intent localIntent = new Intent(application, LocalService.class);
|
||||
//启动守护进程
|
||||
Intent guardIntent = new Intent(application, RemoteService.class);
|
||||
application.startService(localIntent);
|
||||
application.startService(guardIntent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用无声音乐
|
||||
* 如不设置,则默认启用
|
||||
* @param enable
|
||||
*/
|
||||
public static void useSilenceMusice(boolean enable){
|
||||
KeepLive.useSilenceMusice = enable;
|
||||
}
|
||||
|
||||
private static boolean isMain(Application application) {
|
||||
int pid = android.os.Process.myPid();
|
||||
String processName = "";
|
||||
ActivityManager mActivityManager = (ActivityManager) application.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfos = mActivityManager.getRunningAppProcesses();
|
||||
if (runningAppProcessInfos != null) {
|
||||
for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager.getRunningAppProcesses()) {
|
||||
if (appProcess.pid == pid) {
|
||||
processName = appProcess.processName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
String packageName = application.getPackageName();
|
||||
if (processName.equals(packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.fanjun.keeplive.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.view.Gravity;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
public final class OnePixelActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
//设定一像素的activity
|
||||
Window window = getWindow();
|
||||
window.setGravity(Gravity.START | Gravity.TOP);
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
params.x = 0;
|
||||
params.y = 0;
|
||||
params.height = 1;
|
||||
params.width = 1;
|
||||
window.setAttributes(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
checkScreenOn("onResume");
|
||||
}
|
||||
private void checkScreenOn(String methodName) {
|
||||
try{
|
||||
PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
|
||||
boolean isScreenOn = pm.isScreenOn();
|
||||
if (isScreenOn) {
|
||||
finish();
|
||||
}
|
||||
}catch (Exception e){}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.fanjun.keeplive.config;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 默认前台服务样式
|
||||
*/
|
||||
public class ForegroundNotification implements Serializable {
|
||||
private String title;
|
||||
private String description;
|
||||
private int iconRes;
|
||||
private ForegroundNotificationClickListener foregroundNotificationClickListener;
|
||||
private ForegroundNotification(){
|
||||
|
||||
}
|
||||
public ForegroundNotification(String title, String description, int iconRes, ForegroundNotificationClickListener foregroundNotificationClickListener) {
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.iconRes = iconRes;
|
||||
this.foregroundNotificationClickListener = foregroundNotificationClickListener;
|
||||
}
|
||||
|
||||
public ForegroundNotification(String title, String description, int iconRes) {
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.iconRes = iconRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @return ForegroundNotification
|
||||
*/
|
||||
public static ForegroundNotification ini(){
|
||||
return new ForegroundNotification();
|
||||
}
|
||||
/**
|
||||
* 设置标题
|
||||
* @param title 标题
|
||||
* @return ForegroundNotification
|
||||
*/
|
||||
public ForegroundNotification title(@NonNull String title){
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 设置副标题
|
||||
* @param description 副标题
|
||||
* @return ForegroundNotification
|
||||
*/
|
||||
public ForegroundNotification description(@NonNull String description){
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 设置图标
|
||||
* @param iconRes 图标
|
||||
* @return ForegroundNotification
|
||||
*/
|
||||
public ForegroundNotification icon(@NonNull int iconRes){
|
||||
this.iconRes = iconRes;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 设置前台通知点击事件
|
||||
* @param foregroundNotificationClickListener 前台通知点击回调
|
||||
* @return ForegroundNotification
|
||||
*/
|
||||
public ForegroundNotification foregroundNotificationClickListener(@NonNull ForegroundNotificationClickListener foregroundNotificationClickListener){
|
||||
this.foregroundNotificationClickListener = foregroundNotificationClickListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title==null?"":title;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description==null?"":description;
|
||||
}
|
||||
|
||||
public int getIconRes() {
|
||||
return iconRes;
|
||||
}
|
||||
|
||||
public ForegroundNotificationClickListener getForegroundNotificationClickListener() {
|
||||
return foregroundNotificationClickListener;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.fanjun.keeplive.config;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* 前台服务通知点击事件
|
||||
*/
|
||||
public interface ForegroundNotificationClickListener {
|
||||
void foregroundNotificationClick(Context context, Intent intent);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.fanjun.keeplive.config;
|
||||
|
||||
/**
|
||||
* 需要保活的服务
|
||||
*/
|
||||
public interface KeepLiveService {
|
||||
/**
|
||||
* 运行中
|
||||
* 由于服务可能会多次自动启动,该方法可能重复调用
|
||||
*/
|
||||
void onWorking();
|
||||
|
||||
/**
|
||||
* 服务终止
|
||||
* 由于服务可能会被多次终止,该方法可能重复调用,需同onWorking配套使用,如注册和注销
|
||||
*/
|
||||
void onStop();
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.fanjun.keeplive.config;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
|
||||
public class NotificationUtils extends ContextWrapper {
|
||||
private NotificationManager manager;
|
||||
private String id;
|
||||
private String name;
|
||||
private Context context;
|
||||
private NotificationChannel channel;
|
||||
|
||||
private NotificationUtils(Context context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
id = context.getPackageName();
|
||||
name = context.getPackageName();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public void createNotificationChannel() {
|
||||
if (channel == null) {
|
||||
channel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.enableVibration(false);
|
||||
channel.enableLights(false);
|
||||
channel.enableVibration(false);
|
||||
channel.setVibrationPattern(new long[]{0});
|
||||
channel.setSound(null, null);
|
||||
getManager().createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
private NotificationManager getManager() {
|
||||
if (manager == null) {
|
||||
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public Notification.Builder getChannelNotification(String title, String content, int icon, Intent intent) {
|
||||
//PendingIntent.FLAG_UPDATE_CURRENT 这个类型才能传值
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, getPendingIntent());
|
||||
return new Notification.Builder(context, id)
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setSmallIcon(icon)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(pendingIntent);
|
||||
}
|
||||
|
||||
private int getPendingIntent() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
}
|
||||
|
||||
public NotificationCompat.Builder getNotification_25(String title, String content, int icon, Intent intent) {
|
||||
//PendingIntent.FLAG_UPDATE_CURRENT 这个类型才能传值
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, getPendingIntent());
|
||||
return new NotificationCompat.Builder(context, id)
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setSmallIcon(icon)
|
||||
.setAutoCancel(true)
|
||||
.setVibrate(new long[]{0})
|
||||
.setContentIntent(pendingIntent);
|
||||
}
|
||||
|
||||
public static void sendNotification(@NonNull Context context, @NonNull String title, @NonNull String content, @NonNull int icon, @NonNull Intent intent) {
|
||||
NotificationUtils notificationUtils = new NotificationUtils(context);
|
||||
Notification notification = null;
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
notificationUtils.createNotificationChannel();
|
||||
notification = notificationUtils.getChannelNotification(title, content, icon, intent).build();
|
||||
} else {
|
||||
notification = notificationUtils.getNotification_25(title, content, icon, intent).build();
|
||||
}
|
||||
notificationUtils.getManager().notify(new java.util.Random().nextInt(10000), notification);
|
||||
}
|
||||
|
||||
public static Notification createNotification(@NonNull Context context, @NonNull String title, @NonNull String content, @NonNull int icon, @NonNull Intent intent) {
|
||||
NotificationUtils notificationUtils = new NotificationUtils(context);
|
||||
Notification notification = null;
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
notificationUtils.createNotificationChannel();
|
||||
notification = notificationUtils.getChannelNotification(title, content, icon,intent).build();
|
||||
} else {
|
||||
notification = notificationUtils.getNotification_25(title, content, icon,intent).build();
|
||||
}
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.fanjun.keeplive.receiver;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.fanjun.keeplive.KeepLive;
|
||||
|
||||
public final class NotificationClickReceiver extends BroadcastReceiver {
|
||||
public final static String CLICK_NOTIFICATION = "CLICK_NOTIFICATION";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(NotificationClickReceiver.CLICK_NOTIFICATION)) {
|
||||
if (KeepLive.foregroundNotification != null) {
|
||||
if (KeepLive.foregroundNotification.getForegroundNotificationClickListener() != null) {
|
||||
KeepLive.foregroundNotification.getForegroundNotificationClickListener().foregroundNotificationClick(context, intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.fanjun.keeplive.receiver;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.fanjun.keeplive.activity.OnePixelActivity;
|
||||
|
||||
public final class OnepxReceiver extends BroadcastReceiver {
|
||||
android.os.Handler mHander;
|
||||
boolean screenOn = true;
|
||||
|
||||
public OnepxReceiver() {
|
||||
mHander = new android.os.Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { //屏幕关闭的时候接受到广播
|
||||
screenOn = false;
|
||||
mHander.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if(!screenOn){
|
||||
Intent intent2 = new Intent(context, OnePixelActivity.class);
|
||||
intent2.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent2, 0);
|
||||
try {
|
||||
pendingIntent.send();
|
||||
/*} catch (PendingIntent.CanceledException e) {*/
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
},1000);
|
||||
//通知屏幕已关闭,开始播放无声音乐
|
||||
context.sendBroadcast(new Intent("_ACTION_SCREEN_OFF"));
|
||||
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { //屏幕打开的时候发送广播 结束一像素
|
||||
screenOn = true;
|
||||
//通知屏幕已点亮,停止播放无声音乐
|
||||
context.sendBroadcast(new Intent("_ACTION_SCREEN_ON"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.fanjun.keeplive.service;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
|
||||
import com.fanjun.keeplive.KeepLive;
|
||||
import com.fanjun.keeplive.config.NotificationUtils;
|
||||
import com.fanjun.keeplive.receiver.NotificationClickReceiver;
|
||||
|
||||
/**
|
||||
* 隐藏前台服务通知
|
||||
*/
|
||||
public class HideForegroundService extends Service {
|
||||
private android.os.Handler handler;
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
startForeground();
|
||||
if (handler == null){
|
||||
handler = new Handler();
|
||||
}
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
}
|
||||
}, 2000);
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
|
||||
private void startForeground() {
|
||||
if (KeepLive.foregroundNotification != null) {
|
||||
Intent intent = new Intent(getApplicationContext(), NotificationClickReceiver.class);
|
||||
intent.setAction(NotificationClickReceiver.CLICK_NOTIFICATION);
|
||||
Notification notification = NotificationUtils.createNotification(this, KeepLive.foregroundNotification.getTitle(), KeepLive.foregroundNotification.getDescription(), KeepLive.foregroundNotification.getIconRes(), intent);
|
||||
startForeground(13691, notification);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by Fernflower decompiler)
|
||||
//
|
||||
|
||||
package com.fanjun.keeplive.service;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.app.job.JobService;
|
||||
import android.app.job.JobInfo.Builder;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build.VERSION;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.fanjun.keeplive.KeepLive;
|
||||
import com.fanjun.keeplive.config.NotificationUtils;
|
||||
import com.fanjun.keeplive.receiver.NotificationClickReceiver;
|
||||
import com.fanjun.keeplive.utils.ServiceUtils;
|
||||
|
||||
@RequiresApi(
|
||||
api = 21
|
||||
)
|
||||
public final class JobHandlerService extends JobService {
|
||||
private JobScheduler mJobScheduler;
|
||||
private int jobId = 100;
|
||||
|
||||
public JobHandlerService() {
|
||||
}
|
||||
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
this.startService(this);
|
||||
if (VERSION.SDK_INT >= 21) {
|
||||
this.mJobScheduler = (JobScheduler) this.getSystemService("jobscheduler");
|
||||
this.mJobScheduler.cancel(this.jobId);
|
||||
Builder builder = new Builder(this.jobId, new ComponentName(this.getPackageName(), JobHandlerService.class.getName()));
|
||||
if (VERSION.SDK_INT >= 24) {
|
||||
builder.setMinimumLatency(30000L);
|
||||
builder.setOverrideDeadline(30000L);
|
||||
builder.setMinimumLatency(30000L);
|
||||
builder.setBackoffCriteria(30000L, 0);
|
||||
} else {
|
||||
builder.setPeriodic(30000L);
|
||||
}
|
||||
|
||||
builder.setRequiredNetworkType(1);
|
||||
builder.setPersisted(true);
|
||||
this.mJobScheduler.schedule(builder.build());
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private void startService(Context context) {
|
||||
Intent localIntent;
|
||||
if (VERSION.SDK_INT >= 26 && KeepLive.foregroundNotification != null) {
|
||||
localIntent = new Intent(this.getApplicationContext(), NotificationClickReceiver.class);
|
||||
localIntent.setAction("CLICK_NOTIFICATION");
|
||||
Notification notification = NotificationUtils.createNotification(this, KeepLive.foregroundNotification.getTitle(), KeepLive.foregroundNotification.getDescription(), KeepLive.foregroundNotification.getIconRes(), localIntent);
|
||||
this.startForeground(13691, notification);
|
||||
}
|
||||
|
||||
localIntent = new Intent(context, LocalService.class);
|
||||
Intent guardIntent = new Intent(context, RemoteService.class);
|
||||
this.startService(localIntent);
|
||||
this.startService(guardIntent);
|
||||
}
|
||||
|
||||
public boolean onStartJob(JobParameters jobParameters) {
|
||||
if (!ServiceUtils.isServiceRunning(this.getApplicationContext(), "com.fanjun.keeplive.service.LocalService") || !ServiceUtils.isRunningTaskExist(this.getApplicationContext(), this.getPackageName() + ":remote")) {
|
||||
this.startService(this);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onStopJob(JobParameters jobParameters) {
|
||||
if (!ServiceUtils.isServiceRunning(this.getApplicationContext(), "com.fanjun.keeplive.service.LocalService") || !ServiceUtils.isRunningTaskExist(this.getApplicationContext(), this.getPackageName() + ":remote")) {
|
||||
this.startService(this);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package com.fanjun.keeplive.service;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.fanjun.keeplive.KeepLive;
|
||||
import com.fanjun.keeplive.R;
|
||||
import com.fanjun.keeplive.config.NotificationUtils;
|
||||
import com.fanjun.keeplive.receiver.NotificationClickReceiver;
|
||||
import com.fanjun.keeplive.receiver.OnepxReceiver;
|
||||
import com.fanjun.keeplive.utils.ServiceUtils;
|
||||
|
||||
public final class LocalService extends Service {
|
||||
private OnepxReceiver mOnepxReceiver;
|
||||
private ScreenStateReceiver screenStateReceiver;
|
||||
private boolean isPause = true;//控制暂停
|
||||
private MediaPlayer mediaPlayer;
|
||||
private MyBilder mBilder;
|
||||
private android.os.Handler handler;
|
||||
private boolean mIsBoundRemoteService ;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
if (mBilder == null) {
|
||||
mBilder = new MyBilder();
|
||||
}
|
||||
PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
|
||||
isPause = pm.isScreenOn();
|
||||
if (handler == null) {
|
||||
handler = new Handler();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mBilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (KeepLive.useSilenceMusice){
|
||||
//播放无声音乐
|
||||
if (mediaPlayer == null) {
|
||||
mediaPlayer = MediaPlayer.create(this, R.raw.novioce);
|
||||
if (mediaPlayer!= null){
|
||||
mediaPlayer.setVolume(0f, 0f);
|
||||
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mediaPlayer) {
|
||||
if (!isPause) {
|
||||
if (KeepLive.runMode == KeepLive.RunMode.ROGUE) {
|
||||
play();
|
||||
} else {
|
||||
if (handler != null) {
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
play();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
|
||||
@Override
|
||||
public boolean onError(MediaPlayer mp, int what, int extra) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
play();
|
||||
}
|
||||
}
|
||||
}
|
||||
//像素保活
|
||||
if (mOnepxReceiver == null) {
|
||||
mOnepxReceiver = new OnepxReceiver();
|
||||
}
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction("android.intent.action.SCREEN_OFF");
|
||||
intentFilter.addAction("android.intent.action.SCREEN_ON");
|
||||
registerReceiver(mOnepxReceiver, intentFilter);
|
||||
//屏幕点亮状态监听,用于单独控制音乐播放
|
||||
if (screenStateReceiver == null) {
|
||||
screenStateReceiver = new ScreenStateReceiver();
|
||||
}
|
||||
IntentFilter intentFilter2 = new IntentFilter();
|
||||
intentFilter2.addAction("_ACTION_SCREEN_OFF");
|
||||
intentFilter2.addAction("_ACTION_SCREEN_ON");
|
||||
registerReceiver(screenStateReceiver, intentFilter2);
|
||||
//启用前台服务,提升优先级
|
||||
if (KeepLive.foregroundNotification != null) {
|
||||
Intent intent2 = new Intent(getApplicationContext(), NotificationClickReceiver.class);
|
||||
intent2.setAction(NotificationClickReceiver.CLICK_NOTIFICATION);
|
||||
Notification notification = NotificationUtils.createNotification(this, KeepLive.foregroundNotification.getTitle(), KeepLive.foregroundNotification.getDescription(), KeepLive.foregroundNotification.getIconRes(), intent2);
|
||||
startForeground(13691, notification);
|
||||
}
|
||||
//绑定守护进程
|
||||
try {
|
||||
Intent intent3 = new Intent(this, RemoteService.class);
|
||||
mIsBoundRemoteService = this.bindService(intent3, connection, Context.BIND_ABOVE_CLIENT);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
//隐藏服务通知
|
||||
try {
|
||||
if(Build.VERSION.SDK_INT < 25){
|
||||
startService(new Intent(this, HideForegroundService.class));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
if (KeepLive.keepLiveService != null) {
|
||||
KeepLive.keepLiveService.onWorking();
|
||||
}
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
private void play() {
|
||||
if (KeepLive.useSilenceMusice){
|
||||
if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
|
||||
mediaPlayer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void pause() {
|
||||
if (KeepLive.useSilenceMusice){
|
||||
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
|
||||
mediaPlayer.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ScreenStateReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
if (intent.getAction().equals("_ACTION_SCREEN_OFF")) {
|
||||
isPause = false;
|
||||
play();
|
||||
} else if (intent.getAction().equals("_ACTION_SCREEN_ON")) {
|
||||
isPause = true;
|
||||
pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class MyBilder extends GuardAidl.Stub {
|
||||
|
||||
@Override
|
||||
public void wakeUp(String title, String discription, int iconRes) throws RemoteException {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private ServiceConnection connection = new ServiceConnection() {
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
if (ServiceUtils.isServiceRunning(getApplicationContext(), "com.fanjun.keeplive.service.LocalService")){
|
||||
Intent remoteService = new Intent(LocalService.this,
|
||||
RemoteService.class);
|
||||
LocalService.this.startService(remoteService);
|
||||
Intent intent = new Intent(LocalService.this, RemoteService.class);
|
||||
mIsBoundRemoteService = LocalService.this.bindService(intent, connection,
|
||||
Context.BIND_ABOVE_CLIENT);
|
||||
}
|
||||
PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
|
||||
boolean isScreenOn = pm.isScreenOn();
|
||||
if (isScreenOn) {
|
||||
sendBroadcast(new Intent("_ACTION_SCREEN_ON"));
|
||||
} else {
|
||||
sendBroadcast(new Intent("_ACTION_SCREEN_OFF"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
try {
|
||||
if (mBilder != null && KeepLive.foregroundNotification != null) {
|
||||
GuardAidl guardAidl = GuardAidl.Stub.asInterface(service);
|
||||
guardAidl.wakeUp(KeepLive.foregroundNotification.getTitle(), KeepLive.foregroundNotification.getDescription(), KeepLive.foregroundNotification.getIconRes());
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (connection != null){
|
||||
try {
|
||||
if (mIsBoundRemoteService){
|
||||
unbindService(connection);
|
||||
}
|
||||
}catch (Exception e){}
|
||||
}
|
||||
try {
|
||||
unregisterReceiver(mOnepxReceiver);
|
||||
unregisterReceiver(screenStateReceiver);
|
||||
}catch (Exception e){}
|
||||
if (KeepLive.keepLiveService != null) {
|
||||
KeepLive.keepLiveService.onStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.fanjun.keeplive.service;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.fanjun.keeplive.config.NotificationUtils;
|
||||
import com.fanjun.keeplive.receiver.NotificationClickReceiver;
|
||||
import com.fanjun.keeplive.utils.ServiceUtils;
|
||||
|
||||
/**
|
||||
* 守护进程
|
||||
*/
|
||||
@SuppressWarnings(value={"unchecked", "deprecation"})
|
||||
public final class RemoteService extends Service {
|
||||
private MyBilder mBilder;
|
||||
private boolean mIsBoundLocalService ;
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
if (mBilder == null){
|
||||
mBilder = new MyBilder();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mBilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
try {
|
||||
mIsBoundLocalService = this.bindService(new Intent(RemoteService.this, LocalService.class),
|
||||
connection, Context.BIND_ABOVE_CLIENT);
|
||||
}catch (Exception e){
|
||||
}
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (connection != null){
|
||||
try {
|
||||
if (mIsBoundLocalService){
|
||||
unbindService(connection);
|
||||
}
|
||||
}catch (Exception e){}
|
||||
}
|
||||
}
|
||||
private final class MyBilder extends GuardAidl.Stub {
|
||||
|
||||
@Override
|
||||
public void wakeUp(String title, String discription, int iconRes) throws RemoteException {
|
||||
if(Build.VERSION.SDK_INT < 25){
|
||||
Intent intent2 = new Intent(getApplicationContext(), NotificationClickReceiver.class);
|
||||
intent2.setAction(NotificationClickReceiver.CLICK_NOTIFICATION);
|
||||
Notification notification = NotificationUtils.createNotification(RemoteService.this, title, discription, iconRes, intent2);
|
||||
RemoteService.this.startForeground(13691, notification);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final ServiceConnection connection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
if (ServiceUtils.isRunningTaskExist(getApplicationContext(), getPackageName() + ":remote")){
|
||||
Intent localService = new Intent(RemoteService.this,
|
||||
LocalService.class);
|
||||
RemoteService.this.startService(localService);
|
||||
mIsBoundLocalService = RemoteService.this.bindService(new Intent(RemoteService.this,
|
||||
LocalService.class), connection, Context.BIND_ABOVE_CLIENT);
|
||||
}
|
||||
PowerManager pm = (PowerManager) RemoteService.this.getSystemService(Context.POWER_SERVICE);
|
||||
boolean isScreenOn = pm.isScreenOn();
|
||||
if (isScreenOn){
|
||||
sendBroadcast(new Intent("_ACTION_SCREEN_ON"));
|
||||
}else{
|
||||
sendBroadcast(new Intent("_ACTION_SCREEN_OFF"));
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.fanjun.keeplive.utils;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class ServiceUtils {
|
||||
public static boolean isServiceRunning(Context ctx, String className) {
|
||||
boolean isRunning = false;
|
||||
ActivityManager activityManager = (ActivityManager) ctx
|
||||
.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningServiceInfo> servicesList = activityManager
|
||||
.getRunningServices(Integer.MAX_VALUE);
|
||||
if (servicesList != null) {
|
||||
Iterator<ActivityManager.RunningServiceInfo> l = servicesList.iterator();
|
||||
while (l.hasNext()) {
|
||||
ActivityManager.RunningServiceInfo si = l.next();
|
||||
if (className.equals(si.service.getClassName())) {
|
||||
isRunning = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isRunning;
|
||||
}
|
||||
public static boolean isRunningTaskExist(Context context, String processName) {
|
||||
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningAppProcessInfo> processList = am.getRunningAppProcesses();
|
||||
if (processList != null){
|
||||
for (ActivityManager.RunningAppProcessInfo info : processList) {
|
||||
if (info.processName.equals(processName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
<resources>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<resources>
|
||||
<!--1像素保活透明Activity-->
|
||||
<style name="onePixelActivity">
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user