10 | * author: Blankj 11 | * blog : http://blankj.com 12 | * time : 2016/8/2 13 | * desc : App相关工具类 14 | *15 | */ 16 | public class AppUtils { 17 | 18 | // public static void installApk(Context context, File file) { 19 | // Intent intent = new Intent(Intent.ACTION_VIEW); 20 | // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 21 | // Uri data; 22 | // // 判断版本大于等于7.0 23 | // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 24 | // // "net.csdn.blog.ruancoder.fileprovider"即是在清单文件中配置的authorities 25 | // data = FileProvider.getUriForFile(context, "org.sdk.coolfar_sdk.fileprovider", file); 26 | // // 给目标应用一个临时授权 27 | // intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 28 | // } else { 29 | // data = Uri.fromFile(file); 30 | // } 31 | // intent.setDataAndType(data, "application/vnd.android.package-archive"); 32 | // context.startActivity(intent); 33 | // } 34 | 35 | // public static void upDateApp(Context context, File file) { 36 | // installApk(context, file); 37 | // android.os.Process.killProcess(android.os.Process.myPid()); 38 | // } 39 | 40 | private static final String KEY_SERIALNUMBER = "key_serialnumber"; 41 | 42 | /** 43 | * uuid 获取uuid 44 | * @return 45 | */ 46 | public static String getUUID() { 47 | String uuid = AndroidUtil.getMacAddress(DaemonEnv.app); 48 | if(TextUtils.isEmpty(uuid) || "02:00:00:00:00:00".equals(uuid)) { 49 | uuid = AndroidUtil.getSerialNumber(); 50 | } 51 | if(TextUtils.isEmpty(uuid)) { 52 | uuid = SpManager.getInstance().getString(KEY_SERIALNUMBER); 53 | if(TextUtils.isEmpty(uuid)) { 54 | uuid = RandomUtil.getRandom(RandomUtil.NUMBERS_AND_LETTERS, 32); 55 | SpManager.getInstance().putString(KEY_SERIALNUMBER, uuid); 56 | } 57 | } 58 | return uuid; 59 | } 60 | 61 | public static void checkUUID() { 62 | String localUUID = SpManager.getInstance().getString(KEY_SERIALNUMBER); 63 | if(!TextUtils.isEmpty(localUUID) && !localUUID.equals(getUUID())) { 64 | SpManager.getInstance().putString(KEY_SERIALNUMBER, null); 65 | } 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import android.util.Log; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.InputStreamReader; 11 | import java.io.RandomAccessFile; 12 | 13 | /** 14 | * Created by Ray on 2020/1/6. 15 | */ 16 | public class FileUtils { 17 | private static final String TAG = "FileUtils"; 18 | 19 | //包名地址 20 | public static final String FILE_PKG_DIRRECT = "/mnt/sdcard/keepalive/"; 21 | public static final String FILE_SERVICE_NAME = "service.txt"; 22 | public static final String FILE_PKG_PATH = FILE_PKG_DIRRECT + FILE_SERVICE_NAME; 23 | 24 | public static String readTxtFile(String path) { 25 | StringBuilder content = new StringBuilder(); //文件内容字符串 26 | File file = new File(path); //打开文件 27 | if (!file.exists()) { 28 | return content.toString(); 29 | } 30 | if (file.isDirectory()) //如果path是传递过来的参数,可以做一个非目录的判断 31 | { 32 | Log.d("TestFile", "The File doesn't not exist."); 33 | } else { 34 | try { 35 | InputStream instream = new FileInputStream(file); 36 | InputStreamReader inputreader = new InputStreamReader(instream); 37 | BufferedReader buffreader = new BufferedReader(inputreader); 38 | String line; 39 | //分行读取 40 | while ((line = buffreader.readLine()) != null) { 41 | content.append(line); 42 | } 43 | instream.close(); 44 | } catch (java.io.FileNotFoundException e) { 45 | Log.d("TestFile", "The File doesn't not exist."); 46 | } catch (IOException e) { 47 | Log.d("TestFile", e.getMessage()); 48 | } 49 | } 50 | return content.toString(); 51 | } 52 | 53 | 54 | public static void writeTxtToFile(final String strcontent, final String path, final String name) { 55 | new Thread(new Runnable() { 56 | @Override 57 | public void run() { 58 | makeFilePath(path, name); 59 | String strFilePath = path + name; 60 | if (!strFilePath.contains(".txt")) { 61 | strFilePath += ".txt"; 62 | } 63 | // String strContent = TimeUtils.milliseconds2String(System.currentTimeMillis()) + strcontent + "\r\n"; 64 | try { 65 | File file = new File(strFilePath); 66 | if (file.exists()) { 67 | file.delete(); 68 | } 69 | file.getParentFile().mkdirs(); 70 | file.createNewFile(); 71 | RandomAccessFile raf = new RandomAccessFile(file, "rwd"); 72 | raf.seek(file.length()); 73 | raf.write(strcontent.getBytes("UTF-8")); 74 | raf.close(); 75 | } catch (Exception e) { 76 | Log.e("TestFile", "Error on write File:" + e); 77 | } 78 | } 79 | }).start(); 80 | } 81 | 82 | 83 | private static File makeFilePath(String filePath, String fileName) { 84 | File file = null; 85 | makeRootDirectory(filePath); 86 | try { 87 | file = new File(filePath + fileName); 88 | if (!file.exists()) { 89 | file.createNewFile(); 90 | } 91 | } catch (Exception e) { 92 | e.printStackTrace(); 93 | } 94 | return file; 95 | } 96 | 97 | private static void makeRootDirectory(String filePath) { 98 | File file = null; 99 | try { 100 | file = new File(filePath); 101 | if (!file.exists()) { 102 | file.mkdir(); 103 | } 104 | } catch (Exception e) { 105 | Log.i("error:", e + ""); 106 | } 107 | } 108 | 109 | } -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/ForegroundNotificationUtils.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationChannel; 5 | import android.app.NotificationManager; 6 | import android.app.Service; 7 | import android.content.Context; 8 | import android.graphics.BitmapFactory; 9 | import android.graphics.Color; 10 | import android.os.Build; 11 | 12 | import com.sdk.keepbackground.R; 13 | 14 | public class ForegroundNotificationUtils { 15 | // 通知渠道的id 16 | private static final String CHANNEL_ID = "保活图腾"; 17 | private static final int CHANNEL_POSITION = 1; 18 | public static void startForegroundNotification(Service service){ 19 | 20 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ 21 | //启动前台服务而不显示通知的漏洞已在 API Level 25 修复 22 | NotificationManager manager = (NotificationManager)service.getSystemService(Context.NOTIFICATION_SERVICE); 23 | NotificationChannel Channel = new NotificationChannel(CHANNEL_ID,"主服务",NotificationManager.IMPORTANCE_DEFAULT); 24 | Channel.enableLights(true);//设置提示灯 25 | Channel.setLightColor(Color.GREEN);//设置提示灯颜色 26 | Channel.setShowBadge(true);//显示logo 27 | Channel.setDescription("");//设置描述 28 | Channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); //设置锁屏可见 VISIBILITY_PUBLIC=可见 29 | Channel.enableVibration(false); 30 | Channel.setSound(null,null); 31 | manager.createNotificationChannel(Channel); 32 | 33 | Notification notification = new Notification.Builder(service,CHANNEL_ID) 34 | .setContentTitle("主服务")//标题 35 | .setContentText("运行中...")//内容 36 | .setWhen(System.currentTimeMillis()) 37 | .setSmallIcon(R.drawable.icon1)//小图标一定需要设置,否则会报错(如果不设置它启动服务前台化不会报错,但是你会发现这个通知不会启动),如果是普通通知,不设置必然报错 38 | .setLargeIcon(BitmapFactory.decodeResource(service.getResources(),R.drawable.icon1)) 39 | .build(); 40 | service.startForeground(CHANNEL_POSITION,notification);//服务前台化只能使用startForeground()方法,不能使用 notificationManager.notify(1,notification); 这个只是启动通知使用的,使用这个方法你只需要等待几秒就会发现报错了 41 | }else { 42 | //利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知 43 | // service.startForeground(Foreground_ID, new Notification()); 44 | Notification notification = new Notification.Builder(service) 45 | .setContentTitle("主服务")//设置标题 46 | .setContentText("运行中...")//设置内容 47 | .setWhen(System.currentTimeMillis())//设置创建时间 48 | .setSmallIcon(R.drawable.icon1)//设置状态栏图标 49 | .setLargeIcon(BitmapFactory.decodeResource(service.getResources(),R.drawable.icon1))//设置通知栏图标 50 | .build(); 51 | service.startForeground(CHANNEL_POSITION,notification); 52 | } 53 | } 54 | 55 | public static void deleteForegroundNotification(Service service){ 56 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 57 | NotificationManager mNotificationManager = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE); 58 | NotificationChannel mChannel = mNotificationManager.getNotificationChannel(CHANNEL_ID); 59 | if (null != mChannel) { 60 | mNotificationManager.deleteNotificationChannel(CHANNEL_ID); 61 | } 62 | }else { 63 | NotificationManager notificationManager = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE); 64 | notificationManager.cancel(CHANNEL_POSITION); 65 | } 66 | } 67 | 68 | 69 | // private void oldStartForegNotify(){ 70 | //启动前台服务而不显示通知的漏洞已在 API Level 25 修复,大快人心! 71 | // if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) { 72 | // //利用漏洞在 API Level 17 及以下的 Android 系统中,启动前台服务而不显示通知 73 | // startForeground(HASH_CODE, new Notification()); 74 | // //利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知 75 | // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 76 | // Boolean needStartWorkService = needStartWorkService(null, 0, 0); 77 | // DaemonEnv.startServiceSafely(AbsWorkService.this, 78 | // WorkNotificationService.class, 79 | // needStartWorkService); 80 | // } 81 | // } 82 | // } 83 | 84 | // public static class WorkNotificationService extends Service { 85 | // 86 | // /** 87 | // * 利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知 88 | // */ 89 | // @Override 90 | // public int onStartCommand(Intent intent, int flags, int startId) { 91 | // startForeground(1, new Notification()); 92 | // stopSelf(); 93 | // return START_STICKY; 94 | // } 95 | // 96 | // @Override 97 | // public IBinder onBind(Intent intent) { 98 | // return null; 99 | // } 100 | // } 101 | } 102 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/JumpWindowPemiManagement.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.ActivityNotFoundException; 6 | import android.content.ComponentName; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.content.pm.PackageManager; 10 | import android.net.Uri; 11 | import android.os.Build; 12 | import android.provider.Settings; 13 | import android.support.v7.appcompat.BuildConfig; 14 | import android.util.Log; 15 | 16 | public class JumpWindowPemiManagement { 17 | private static final String TAG = "JumpPermission"; 18 | 19 | /** 20 | * Build.MANUFACTURER 21 | */ 22 | private static final String MANUFACTURER_HUAWEI = "Huawei";//华为 23 | private static final String MANUFACTURER_MEIZU = "Meizu";//魅族 24 | private static final String MANUFACTURER_XIAOMI = "Xiaomi";//小米 25 | private static final String MANUFACTURER_SONY = "Sony";//索尼 26 | private static final String MANUFACTURER_OPPO = "OPPO"; 27 | private static final String MANUFACTURER_LG = "LG"; 28 | private static final String MANUFACTURER_VIVO = "vivo"; 29 | private static final String MANUFACTURER_SAMSUNG = "samsung";//三星 30 | private static final String MANUFACTURER_LETV = "Letv";//乐视 31 | private static final String MANUFACTURER_ZTE = "ZTE";//中兴 32 | private static final String MANUFACTURER_YULONG = "YuLong";//酷派 33 | private static final String MANUFACTURER_LENOVO = "LENOVO";//联想 34 | 35 | /** 36 | * 此函数可以自己定义 37 | * 38 | * @param activity 39 | */ 40 | public static void GoToSetting(Activity activity) { 41 | try { 42 | switch (Build.MANUFACTURER) { 43 | case MANUFACTURER_HUAWEI: 44 | Huawei(activity); 45 | break; 46 | case MANUFACTURER_MEIZU: 47 | Meizu(activity); 48 | break; 49 | case MANUFACTURER_XIAOMI: 50 | Xiaomi(activity); 51 | break; 52 | case MANUFACTURER_SONY: 53 | Sony(activity); 54 | break; 55 | case MANUFACTURER_OPPO: 56 | OPPO(activity); 57 | break; 58 | case MANUFACTURER_LG: 59 | LG(activity); 60 | break; 61 | case MANUFACTURER_LETV: 62 | Letv(activity); 63 | break; 64 | default: 65 | ApplicationInfo(activity); 66 | Log.e(TAG, "目前暂不支持此系统"); 67 | break; 68 | } 69 | } catch (Exception e) { 70 | Log.v(TAG, e.toString()); 71 | goToWindow(activity); 72 | } 73 | } 74 | 75 | public static boolean hasWindowPei(Context context) { 76 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 77 | if (context.checkSelfPermission(Manifest.permission.SYSTEM_ALERT_WINDOW) == PackageManager.PERMISSION_GRANTED || Settings.canDrawOverlays(context)) { 78 | return true; 79 | } 80 | } 81 | return false; 82 | } 83 | 84 | public static void Huawei(Activity activity) { 85 | Intent intent = new Intent(); 86 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 87 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 88 | ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity"); 89 | intent.setComponent(comp); 90 | activity.startActivity(intent); 91 | } 92 | 93 | public static void Meizu(Activity activity) { 94 | Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); 95 | intent.addCategory(Intent.CATEGORY_DEFAULT); 96 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 97 | activity.startActivity(intent); 98 | } 99 | 100 | public static void Xiaomi(Activity activity) { 101 | try { 102 | Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); 103 | ComponentName componentName = new ComponentName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); 104 | intent.setComponent(componentName); 105 | intent.putExtra("extra_pkgname", BuildConfig.APPLICATION_ID); 106 | activity.startActivity(intent); 107 | } catch (Exception e) { 108 | goToWindow(activity); 109 | } 110 | } 111 | 112 | public static void Sony(Activity activity) { 113 | Intent intent = new Intent(); 114 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 115 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 116 | ComponentName comp = new ComponentName("com.sonymobile.cta", "com.sonymobile.cta.SomcCTAMainActivity"); 117 | intent.setComponent(comp); 118 | activity.startActivity(intent); 119 | } 120 | 121 | public static void OPPO(Activity activity) { 122 | Intent intent = new Intent(); 123 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 124 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 125 | ComponentName comp = new ComponentName("com.color.safecenter", "com.color.safecenter.permission.PermissionManagerActivity"); 126 | intent.setComponent(comp); 127 | activity.startActivity(intent); 128 | } 129 | 130 | public static void LG(Activity activity) { 131 | Intent intent = new Intent("android.intent.action.MAIN"); 132 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 133 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 134 | ComponentName comp = new ComponentName("com.android.settings", "com.android.settings.Settings$AccessLockSummaryActivity"); 135 | intent.setComponent(comp); 136 | activity.startActivity(intent); 137 | } 138 | 139 | public static void Letv(Activity activity) { 140 | Intent intent = new Intent(); 141 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 142 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 143 | ComponentName comp = new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.PermissionAndApps"); 144 | intent.setComponent(comp); 145 | activity.startActivity(intent); 146 | } 147 | 148 | /** 149 | * 只能打开到自带安全软件 150 | * 151 | * @param activity 152 | */ 153 | public static void _360(Activity activity) { 154 | Intent intent = new Intent("android.intent.action.MAIN"); 155 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 156 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 157 | ComponentName comp = new ComponentName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.AppEnterActivity"); 158 | intent.setComponent(comp); 159 | activity.startActivity(intent); 160 | } 161 | 162 | public static final int CHECK_PEERISSION_CODE = 1001; 163 | 164 | /** 165 | * 应用信息界面 166 | * 167 | * @param context 168 | */ 169 | public static void ApplicationInfo(Activity context) { 170 | Uri packageURI = Uri.parse("package:" + context.getPackageName()); 171 | Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI); 172 | (context).startActivityForResult(intent, CHECK_PEERISSION_CODE); 173 | } 174 | 175 | /** 176 | * 系统设置界面 177 | * 178 | * @param activity 179 | */ 180 | public static void SystemConfig(Activity activity) { 181 | Intent intent = new Intent(Settings.ACTION_SETTINGS); 182 | activity.startActivity(intent); 183 | } 184 | 185 | public static void goToWindow(Context context) { 186 | try { 187 | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, 188 | Uri.parse("package:" + context.getPackageName())); 189 | context.startActivity(intent); 190 | } catch (ActivityNotFoundException e) { 191 | goToAppSetting(context); 192 | } 193 | } 194 | 195 | //进入应用设置 196 | public static void goToAppSetting(Context context) { 197 | if (!(context instanceof Activity)) return; 198 | Uri packageURI = Uri.parse("package:" + context.getPackageName()); 199 | Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI); 200 | ((Activity) context).startActivityForResult(intent, CHECK_PEERISSION_CODE); 201 | } 202 | } -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/NotificationSetUtil.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.AppOpsManager; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.pm.ApplicationInfo; 8 | import android.net.Uri; 9 | import android.os.Build; 10 | import android.support.annotation.RequiresApi; 11 | import android.support.v4.app.NotificationManagerCompat; 12 | 13 | import java.lang.reflect.Field; 14 | import java.lang.reflect.Method; 15 | 16 | /** 17 | * Created by HaiyuKing 18 | * Used 判断是否开启消息通知,没有开启的话跳转到手机系统设置界面 19 | * 参考:https://blog.csdn.net/lidayingyy/article/details/81778894 20 | * https://www.jianshu.com/p/205bc87cac29 21 | * https://blog.csdn.net/yrmao9893/article/details/74607402/ 22 | * https://www.meiwen.com.cn/subject/qcklpftx.html 23 | */ 24 | public class NotificationSetUtil { 25 | 26 | //判断是否需要打开设置界面 27 | public static void OpenNotificationSetting(Context context, OnNextLitener mOnNextLitener) { 28 | if (!isNotificationEnabled(context)) { 29 | mOnNextLitener.onNext(false); 30 | } else { 31 | if (mOnNextLitener != null) { 32 | mOnNextLitener.onNext(true); 33 | } 34 | } 35 | } 36 | 37 | //判断该app是否打开了通知 38 | /** 39 | * 可以通过NotificationManagerCompat 中的 areNotificationsEnabled()来判断是否开启通知权限。NotificationManagerCompat 在 android.support.v4.app包中,是API 22.1.0 中加入的。而 areNotificationsEnabled()则是在 API 24.1.0之后加入的。 40 | * areNotificationsEnabled 只对 API 19 及以上版本有效,低于API 19 会一直返回true 41 | * */ 42 | @TargetApi(Build.VERSION_CODES.KITKAT) 43 | public static boolean isNotificationEnabled(Context context) { 44 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){ 45 | NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context); 46 | boolean areNotificationsEnabled = notificationManagerCompat.areNotificationsEnabled(); 47 | return areNotificationsEnabled; 48 | } 49 | 50 | String CHECK_OP_NO_THROW = "checkOpNoThrow"; 51 | String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION"; 52 | 53 | AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 54 | ApplicationInfo appInfo = context.getApplicationInfo(); 55 | String pkg = context.getApplicationContext().getPackageName(); 56 | int uid = appInfo.uid; 57 | 58 | Class appOpsClass = null; 59 | /* Context.APP_OPS_MANAGER */ 60 | try { 61 | appOpsClass = Class.forName(AppOpsManager.class.getName()); 62 | Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, 63 | String.class); 64 | Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION); 65 | 66 | int value = (Integer) opPostNotificationValue.get(Integer.class); 67 | return ((Integer) checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg) == AppOpsManager.MODE_ALLOWED); 68 | 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | } 72 | return false; 73 | 74 | } 75 | 76 | //打开手机设置页面 77 | /** 78 | * 假设没有开启通知权限,点击之后就需要跳转到 APP的通知设置界面,对应的Action是:Settings.ACTION_APP_NOTIFICATION_SETTINGS, 这个Action是 API 26 后增加的 79 | * 如果在部分手机中无法精确的跳转到 APP对应的通知设置界面,那么我们就考虑直接跳转到 APP信息界面,对应的Action是:Settings.ACTION_APPLICATION_DETAILS_SETTINGS*/ 80 | public static void gotoSet(Context context) { 81 | 82 | Intent intent = new Intent(); 83 | if (Build.VERSION.SDK_INT >= 26) { 84 | // android 8.0引导 85 | intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); 86 | intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName()); 87 | } else if (Build.VERSION.SDK_INT >= 21) { 88 | // android 5.0-7.0 89 | intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); 90 | intent.putExtra("app_package", context.getPackageName()); 91 | intent.putExtra("app_uid", context.getApplicationInfo().uid); 92 | } else { 93 | // 其他 94 | intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); 95 | intent.setData(Uri.fromParts("package", context.getPackageName(), null)); 96 | } 97 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 98 | context.startActivity(intent); 99 | } 100 | 101 | /*=====================添加Listener回调================================*/ 102 | public interface OnNextLitener { 103 | /** 104 | * 不需要设置通知的下一步 105 | */ 106 | void onNext(Boolean isOpen); 107 | } 108 | 109 | private OnNextLitener mOnNextLitener; 110 | 111 | public void setOnNextLitener(OnNextLitener mOnNextLitener) { 112 | this.mOnNextLitener = mOnNextLitener; 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/RandomUtil.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * 随机工具类 7 | */ 8 | public class RandomUtil { 9 | public static final String NUMBERS_AND_LETTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 10 | public static final String NUMBERS = "0123456789"; 11 | public static final String LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 12 | public static final String CAPITAL_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 13 | public static final String LOWER_CASE_LETTERS = "abcdefghijklmnopqrstuvwxyz"; 14 | 15 | private RandomUtil() { 16 | throw new AssertionError(); 17 | } 18 | 19 | /** 20 | * get a fixed-length random string, its a mixture of uppercase, lowercase letters and numbers 21 | * 22 | * @param length 23 | * @return 24 | * @see RandomUtil#getRandom(String source, int length) 25 | */ 26 | public static String getRandomNumbersAndLetters(int length) { 27 | return getRandom(NUMBERS_AND_LETTERS, length); 28 | } 29 | 30 | /** 31 | * get a fixed-length random string, its a mixture of numbers 32 | * 33 | * @param length 34 | * @return 35 | * @see RandomUtil#getRandom(String source, int length) 36 | */ 37 | public static String getRandomNumbers(int length) { 38 | return getRandom(NUMBERS, length); 39 | } 40 | 41 | /** 42 | * get a fixed-length random string, its a mixture of uppercase and lowercase letters 43 | * 44 | * @param length 45 | * @return 46 | * @see RandomUtil#getRandom(String source, int length) 47 | */ 48 | public static String getRandomLetters(int length) { 49 | return getRandom(LETTERS, length); 50 | } 51 | 52 | /** 53 | * get a fixed-length random string, its a mixture of uppercase letters 54 | * 55 | * @param length 56 | * @return 57 | * @see RandomUtil#getRandom(String source, int length) 58 | */ 59 | public static String getRandomCapitalLetters(int length) { 60 | return getRandom(CAPITAL_LETTERS, length); 61 | } 62 | 63 | /** 64 | * get a fixed-length random string, its a mixture of lowercase letters 65 | * 66 | * @param length 67 | * @return 68 | * @see RandomUtil#getRandom(String source, int length) 69 | */ 70 | public static String getRandomLowerCaseLetters(int length) { 71 | return getRandom(LOWER_CASE_LETTERS, length); 72 | } 73 | 74 | /** 75 | * get a fixed-length random string, its a mixture of chars in source 76 | * 77 | * @param source 78 | * @param length 79 | * @return
13 | * author: Blankj 14 | * blog : http://blankj.com 15 | * time : 2016/8/2 16 | * desc : SP相关工具类 17 | *18 | */ 19 | public class SPUtils { 20 | 21 | private SharedPreferences sp; 22 | private SharedPreferences.Editor editor; 23 | 24 | /** 25 | * SPUtils构造函数 26 | *
在Application中初始化
27 | * 28 | * @param spName spName 29 | */ 30 | public SPUtils(String spName) { 31 | sp = DaemonEnv.app.getSharedPreferences(spName, Context.MODE_PRIVATE); 32 | editor = sp.edit(); 33 | editor.apply(); 34 | } 35 | 36 | /** 37 | * SP中写入String类型value 38 | * 39 | * @param key 键 40 | * @param value 值 41 | */ 42 | public void putString(String key, String value) { 43 | editor.putString(key, value).apply(); 44 | } 45 | 46 | /** 47 | * SP中读取String 48 | * 49 | * @param key 键 50 | * @return 存在返回对应值,不存在返回默认值{@code null} 51 | */ 52 | public String getString(String key) { 53 | return getString(key, null); 54 | } 55 | 56 | /** 57 | * SP中读取String 58 | * 59 | * @param key 键 60 | * @param defaultValue 默认值 61 | * @return 存在返回对应值,不存在返回默认值{@code defaultValue} 62 | */ 63 | public String getString(String key, String defaultValue) { 64 | return sp.getString(key, defaultValue); 65 | } 66 | 67 | /** 68 | * SP中写入int类型value 69 | * 70 | * @param key 键 71 | * @param value 值 72 | */ 73 | public void putInt(String key, int value) { 74 | editor.putInt(key, value).apply(); 75 | } 76 | 77 | /** 78 | * SP中读取int 79 | * 80 | * @param key 键 81 | * @return 存在返回对应值,不存在返回默认值-1 82 | */ 83 | public int getInt(String key) { 84 | return getInt(key, -1); 85 | } 86 | 87 | /** 88 | * SP中读取int 89 | * 90 | * @param key 键 91 | * @param defaultValue 默认值 92 | * @return 存在返回对应值,不存在返回默认值{@code defaultValue} 93 | */ 94 | public int getInt(String key, int defaultValue) { 95 | return sp.getInt(key, defaultValue); 96 | } 97 | 98 | /** 99 | * SP中写入long类型value 100 | * 101 | * @param key 键 102 | * @param value 值 103 | */ 104 | public void putLong(String key, long value) { 105 | editor.putLong(key, value).apply(); 106 | } 107 | 108 | /** 109 | * SP中读取long 110 | * 111 | * @param key 键 112 | * @return 存在返回对应值,不存在返回默认值-1 113 | */ 114 | public long getLong(String key) { 115 | return getLong(key, -1L); 116 | } 117 | 118 | /** 119 | * SP中读取long 120 | * 121 | * @param key 键 122 | * @param defaultValue 默认值 123 | * @return 存在返回对应值,不存在返回默认值{@code defaultValue} 124 | */ 125 | public long getLong(String key, long defaultValue) { 126 | return sp.getLong(key, defaultValue); 127 | } 128 | 129 | /** 130 | * SP中写入float类型value 131 | * 132 | * @param key 键 133 | * @param value 值 134 | */ 135 | public void putFloat(String key, float value) { 136 | editor.putFloat(key, value).apply(); 137 | } 138 | 139 | /** 140 | * SP中读取float 141 | * 142 | * @param key 键 143 | * @return 存在返回对应值,不存在返回默认值-1 144 | */ 145 | public float getFloat(String key) { 146 | return getFloat(key, -1f); 147 | } 148 | 149 | /** 150 | * SP中读取float 151 | * 152 | * @param key 键 153 | * @param defaultValue 默认值 154 | * @return 存在返回对应值,不存在返回默认值{@code defaultValue} 155 | */ 156 | public float getFloat(String key, float defaultValue) { 157 | return sp.getFloat(key, defaultValue); 158 | } 159 | 160 | /** 161 | * SP中写入boolean类型value 162 | * 163 | * @param key 键 164 | * @param value 值 165 | */ 166 | public void putBoolean(String key, boolean value) { 167 | editor.putBoolean(key, value).apply(); 168 | } 169 | 170 | /** 171 | * SP中读取boolean 172 | * 173 | * @param key 键 174 | * @return 存在返回对应值,不存在返回默认值{@code false} 175 | */ 176 | public boolean getBoolean(String key) { 177 | return getBoolean(key, false); 178 | } 179 | 180 | /** 181 | * SP中读取boolean 182 | * 183 | * @param key 键 184 | * @param defaultValue 默认值 185 | * @return 存在返回对应值,不存在返回默认值{@code defaultValue} 186 | */ 187 | public boolean getBoolean(String key, boolean defaultValue) { 188 | return sp.getBoolean(key, defaultValue); 189 | } 190 | 191 | /** 192 | * SP中获取所有键值对 193 | * 194 | * @return Map对象 195 | */ 196 | public Map格式为yyyy-MM-dd HH:mm:ss
57 | * 58 | * @param milliseconds 毫秒时间戳 59 | * @return 时间字符串 60 | */ 61 | public static String milliseconds2String(long milliseconds) { 62 | return milliseconds2String(milliseconds, DEFAULT_SDF); 63 | } 64 | 65 | /** 66 | * 将时间戳转为时间字符串 67 | *格式为用户自定义
68 | * 69 | * @param milliseconds 毫秒时间戳 70 | * @param format 时间格式 71 | * @return 时间字符串 72 | */ 73 | public synchronized static String milliseconds2String(long milliseconds, SimpleDateFormat format) { 74 | return format.format(new Date(milliseconds)); 75 | } 76 | 77 | /** 78 | * 将时间字符串转为时间戳 79 | *格式为yyyy-MM-dd HH:mm:ss
80 | * 81 | * @param time 时间字符串 82 | * @return 毫秒时间戳 83 | */ 84 | public static long string2Milliseconds(String time) { 85 | return string2Milliseconds(time, DEFAULT_SDF); 86 | } 87 | 88 | /** 89 | * 将时间字符串转为时间戳 90 | *格式为用户自定义
91 | * 92 | * @param time 时间字符串 93 | * @param format 时间格式 94 | * @return 毫秒时间戳 95 | */ 96 | public static long string2Milliseconds(String time, SimpleDateFormat format) { 97 | try { 98 | return format.parse(time).getTime(); 99 | } catch (ParseException e) { 100 | e.printStackTrace(); 101 | } 102 | return -1; 103 | } 104 | 105 | /** 106 | * 将时间字符串转为Date类型 107 | *格式为yyyy-MM-dd HH:mm:ss
108 | * 109 | * @param time 时间字符串 110 | * @return Date类型 111 | */ 112 | public static Date string2Date(String time) { 113 | return string2Date(time, DEFAULT_SDF); 114 | } 115 | 116 | /** 117 | * 将时间字符串转为Date类型 118 | *格式为用户自定义
119 | * 120 | * @param time 时间字符串 121 | * @param format 时间格式 122 | * @return Date类型 123 | */ 124 | public static Date string2Date(String time, SimpleDateFormat format) { 125 | return new Date(string2Milliseconds(time, format)); 126 | } 127 | 128 | /** 129 | * 将Date类型转为时间字符串 130 | *格式为yyyy-MM-dd HH:mm:ss
131 | * 132 | * @param time Date类型时间 133 | * @return 时间字符串 134 | */ 135 | public static String date2String(Date time) { 136 | return date2String(time, DEFAULT_SDF); 137 | } 138 | 139 | /** 140 | * 将Date类型转为时间字符串 141 | *格式为用户自定义
142 | * 143 | * @param time Date类型时间 144 | * @param format 时间格式 145 | * @return 时间字符串 146 | */ 147 | public synchronized static String date2String(Date time, SimpleDateFormat format) { 148 | return format.format(time); 149 | } 150 | 151 | /** 152 | * 将Date类型转为时间戳 153 | * 154 | * @param time Date类型时间 155 | * @return 毫秒时间戳 156 | */ 157 | public static long date2Milliseconds(Date time) { 158 | return time.getTime(); 159 | } 160 | 161 | /** 162 | * 将时间戳转为Date类型 163 | * 164 | * @param milliseconds 毫秒时间戳 165 | * @return Date类型时间 166 | */ 167 | public static Date milliseconds2Date(long milliseconds) { 168 | return new Date(milliseconds); 169 | } 170 | 171 | /** 172 | * 毫秒时间戳单位转换(单位:unit) 173 | * 174 | * @param milliseconds 毫秒时间戳 175 | * @param unit 176 | * @return unit时间戳 177 | */ 178 | private static long milliseconds2Unit(long milliseconds, TimeUnit unit) { 179 | switch (unit) { 180 | case MSEC: 181 | return milliseconds / MSEC; 182 | case SEC: 183 | return milliseconds / SEC; 184 | case MIN: 185 | return milliseconds / MIN; 186 | case HOUR: 187 | return milliseconds / HOUR; 188 | case DAY: 189 | return milliseconds / DAY; 190 | default: 191 | } 192 | return -1; 193 | } 194 | 195 | /** 196 | * 获取两个时间差(单位:unit) 197 | *time1和time2格式都为yyyy-MM-dd HH:mm:ss
198 | * 199 | * @param time0 时间字符串1 200 | * @param time1 时间字符串2 201 | * @param unit 202 | * @return unit时间戳 203 | */ 204 | public static long getIntervalTime(String time0, String time1, TimeUnit unit) { 205 | return getIntervalTime(time0, time1, unit, DEFAULT_SDF); 206 | } 207 | 208 | /** 209 | * 获取两个时间差(单位:unit) 210 | *time1和time2格式都为format
211 | * 212 | * @param time0 时间字符串1 213 | * @param time1 时间字符串2 214 | * @param unit 215 | * @param format 时间格式 216 | * @return unit时间戳 217 | */ 218 | public static long getIntervalTime(String time0, String time1, TimeUnit unit, SimpleDateFormat format) { 219 | return milliseconds2Unit(Math.abs(string2Milliseconds(time0, format) 220 | - string2Milliseconds(time1, format)), unit); 221 | } 222 | 223 | /** 224 | * 获取两个时间差(单位:unit) 225 | *time1和time2都为Date类型
226 | * 227 | * @param time0 Date类型时间1 228 | * @param time1 Date类型时间2 229 | * @param unit 230 | * @return unit时间戳 231 | */ 232 | public static long getIntervalTime(Date time0, Date time1, TimeUnit unit) { 233 | return milliseconds2Unit(Math.abs(date2Milliseconds(time1) 234 | - date2Milliseconds(time0)), unit); 235 | } 236 | 237 | /** 238 | * 获取当前时间 239 | * 240 | * @return 毫秒时间戳 241 | */ 242 | public static long getCurTimeMills() { 243 | return System.currentTimeMillis(); 244 | } 245 | 246 | /** 247 | * 获取当前时间 248 | *格式为yyyy-MM-dd HH:mm:ss
249 | * 250 | * @return 时间字符串 251 | */ 252 | public static String getCurTimeString() { 253 | return date2String(new Date()); 254 | } 255 | 256 | /** 257 | * 获取当前时间 258 | *格式为用户自定义
259 | * 260 | * @param format 时间格式 261 | * @return 时间字符串 262 | */ 263 | public static String getCurTimeString(SimpleDateFormat format) { 264 | return date2String(new Date(), format); 265 | } 266 | 267 | /** 268 | * 获取当前时间 269 | *Date类型
270 | * 271 | * @return Date类型时间 272 | */ 273 | public static Date getCurTimeDate() { 274 | return new Date(); 275 | } 276 | 277 | /** 278 | * 获取与当前时间的差(单位:unit) 279 | *time格式为yyyy-MM-dd HH:mm:ss
280 | * 281 | * @param time 时间字符串 282 | * @param unit 283 | * @return unit时间戳 284 | */ 285 | public static long getIntervalByNow(String time, TimeUnit unit) { 286 | return getIntervalByNow(time, unit, DEFAULT_SDF); 287 | } 288 | 289 | /** 290 | * 获取与当前时间的差(单位:unit) 291 | *time格式为format
292 | * 293 | * @param time 时间字符串 294 | * @param unit 295 | * @param format 时间格式 296 | * @return unit时间戳 297 | */ 298 | public static long getIntervalByNow(String time, TimeUnit unit, SimpleDateFormat format) { 299 | return getIntervalTime(getCurTimeString(), time, unit, format); 300 | } 301 | 302 | /** 303 | * 获取与当前时间的差(单位:unit) 304 | *time为Date类型
305 | * 306 | * @param time Date类型时间 307 | * @param unit 308 | * @return unit时间戳 309 | */ 310 | public static long getIntervalByNow(Date time, TimeUnit unit) { 311 | return getIntervalTime(getCurTimeDate(), time, unit); 312 | } 313 | 314 | /** 315 | * 判断闰年 316 | * 317 | * @param year 年份 318 | * @return {@code true}: 闰年time格式为yyyy-MM-dd HH:mm:ss
327 | * 328 | * @param time 时间字符串 329 | * @return 星期 330 | */ 331 | public static String getWeek(String time) { 332 | return new SimpleDateFormat("EEEE", Locale.getDefault()).format(string2Date(time)); 333 | } 334 | 335 | /** 336 | * 获取星期 337 | * 338 | * @param time 时间字符串 339 | * @param format 时间格式 340 | * @return 星期 341 | */ 342 | public static String getWeek(String time, SimpleDateFormat format) { 343 | return new SimpleDateFormat("EEEE", Locale.getDefault()).format(string2Date(time, format)); 344 | } 345 | 346 | /** 347 | * 获取星期 348 | * 349 | * @param time Date类型时间 350 | * @return 星期 351 | */ 352 | public static String getWeek(Date time) { 353 | return new SimpleDateFormat("EEEE", Locale.getDefault()).format(time); 354 | } 355 | 356 | /** 357 | * 获取星期 358 | *注意:周日的Index才是1,周六为7
359 | *time格式为yyyy-MM-dd HH:mm:ss
360 | * 361 | * @param time 时间字符串 362 | * @return 1...5 363 | */ 364 | public static int getWeekIndex(String time) { 365 | Date date = string2Date(time); 366 | return getWeekIndex(date); 367 | } 368 | 369 | /** 370 | * 获取星期 371 | *注意:周日的Index才是1,周六为7
372 | * 373 | * @param time 时间字符串 374 | * @param format 时间格式 375 | * @return 1...7 376 | */ 377 | public static int getWeekIndex(String time, SimpleDateFormat format) { 378 | Date date = string2Date(time, format); 379 | return getWeekIndex(date); 380 | } 381 | 382 | /** 383 | * 获取星期 384 | *注意:周日的Index才是1,周六为7
385 | * 386 | * @param time Date类型时间 387 | * @return 1...7 388 | */ 389 | public static int getWeekIndex(Date time) { 390 | Calendar cal = Calendar.getInstance(); 391 | cal.setTime(time); 392 | return cal.get(Calendar.DAY_OF_WEEK); 393 | } 394 | 395 | /** 396 | * 获取月份中的第几周 397 | *注意:国外周日才是新的一周的开始
398 | *time格式为yyyy-MM-dd HH:mm:ss
399 | * 400 | * @param time 时间字符串 401 | * @return 1...5 402 | */ 403 | public static int getWeekOfMonth(String time) { 404 | Date date = string2Date(time); 405 | return getWeekOfMonth(date); 406 | } 407 | 408 | /** 409 | * 获取月份中的第几周 410 | *注意:国外周日才是新的一周的开始
411 | * 412 | * @param time 时间字符串 413 | * @param format 时间格式 414 | * @return 1...5 415 | */ 416 | public static int getWeekOfMonth(String time, SimpleDateFormat format) { 417 | Date date = string2Date(time, format); 418 | return getWeekOfMonth(date); 419 | } 420 | 421 | /** 422 | * 获取月份中的第几周 423 | *注意:国外周日才是新的一周的开始
424 | * 425 | * @param time Date类型时间 426 | * @return 1...5 427 | */ 428 | public static int getWeekOfMonth(Date time) { 429 | Calendar cal = Calendar.getInstance(); 430 | cal.setTime(time); 431 | return cal.get(Calendar.WEEK_OF_MONTH); 432 | } 433 | 434 | /** 435 | * 获取年份中的第几周 436 | *注意:国外周日才是新的一周的开始
437 | *time格式为yyyy-MM-dd HH:mm:ss
438 | * 439 | * @param time 时间字符串 440 | * @return 1...54 441 | */ 442 | public static int getWeekOfYear(String time) { 443 | Date date = string2Date(time); 444 | return getWeekOfYear(date); 445 | } 446 | 447 | /** 448 | * 获取年份中的第几周 449 | *注意:国外周日才是新的一周的开始
450 | * 451 | * @param time 时间字符串 452 | * @param format 时间格式 453 | * @return 1...54 454 | */ 455 | public static int getWeekOfYear(String time, SimpleDateFormat format) { 456 | Date date = string2Date(time, format); 457 | return getWeekOfYear(date); 458 | } 459 | 460 | /** 461 | * 获取年份中的第几周 462 | *注意:国外周日才是新的一周的开始
463 | * 464 | * @param time Date类型时间 465 | * @return 1...54 466 | */ 467 | public static int getWeekOfYear(Date time) { 468 | Calendar cal = Calendar.getInstance(); 469 | cal.setTime(time); 470 | return cal.get(Calendar.WEEK_OF_YEAR); 471 | } 472 | 473 | public static String string2String(String time, SimpleDateFormat fromFormat, SimpleDateFormat toFormat) { 474 | return TimeUtils.date2String(TimeUtils.string2Date(time, fromFormat), toFormat); 475 | } 476 | 477 | public static boolean isBefore(Date d1, Date d2) { 478 | return d1.before(d2); 479 | } 480 | 481 | public static boolean isBefore(String d1, String d2, SimpleDateFormat sdf) { 482 | try { 483 | return sdf.parse(d1).before(sdf.parse(d2)); 484 | } catch (Exception e) { 485 | return false; 486 | } 487 | } 488 | 489 | /** 490 | * 获取某个日期的前几天的日期 491 | * 492 | * @param dateString 某日期 493 | * @param dayNumber 前面第几天 494 | * @return 495 | */ 496 | public static String getPreviousDay(String dateString, int dayNumber) { 497 | Calendar c = Calendar.getInstance(); 498 | Date date = null; 499 | try { 500 | date = new SimpleDateFormat("yyyy-MM-dd").parse(dateString); 501 | } catch (ParseException e) { 502 | e.printStackTrace(); 503 | } 504 | c.setTime(date); 505 | int day = c.get(Calendar.DATE); 506 | c.set(Calendar.DATE, day - dayNumber); 507 | 508 | String previousDay = new SimpleDateFormat("yyyy-MM-dd").format(c.getTime()); 509 | return previousDay; 510 | } 511 | 512 | public static String getSystemTime() { 513 | return String.valueOf(System.currentTimeMillis()); 514 | } 515 | 516 | /** 517 | * 判断当前时间是否在两个时间段之内 518 | * 519 | * @param beginTime 开始时间 520 | * @param endTime 结束时间 521 | * @return 522 | */ 523 | public static boolean isNowBetween(String beginTime, String endTime) { 524 | SimpleDateFormat df = new SimpleDateFormat("HH:mm"); 525 | Date now = null; 526 | Date begin = null; 527 | Date end = null; 528 | try { 529 | now = df.parse(df.format(new Date())); 530 | begin = df.parse(beginTime); 531 | end = df.parse(endTime); 532 | } catch (Exception e) { 533 | e.printStackTrace(); 534 | } 535 | Calendar nowCal = Calendar.getInstance(); 536 | nowCal.setTime(now); 537 | 538 | Calendar beginCal = Calendar.getInstance(); 539 | beginCal.setTime(begin); 540 | 541 | Calendar endCal = Calendar.getInstance(); 542 | endCal.setTime(end); 543 | 544 | if (nowCal.after(beginCal) && nowCal.before(endCal)) { 545 | return true; 546 | } else { 547 | return false; 548 | } 549 | } 550 | 551 | /** 552 | * 获取当前时间在某段时间内的百分比位置 553 | * 554 | * @param beginTime 开始时间 555 | * @param endTime 结束时间 556 | * @return 557 | */ 558 | public static float getTimeDiffPercent(String beginTime, String endTime) { 559 | SimpleDateFormat df = new SimpleDateFormat("HH:mm"); 560 | Date now = null; 561 | Date begin = null; 562 | Date end = null; 563 | try { 564 | now = df.parse(df.format(new Date())); 565 | begin = df.parse(beginTime); 566 | end = df.parse(endTime); 567 | } catch (Exception e) { 568 | e.printStackTrace(); 569 | } 570 | return (float) (now.getTime() - begin.getTime()) / (float) (end.getTime() - begin.getTime()); 571 | } 572 | public enum TimeUnit { 573 | MSEC, 574 | SEC, 575 | MIN, 576 | HOUR, 577 | DAY 578 | } 579 | } 580 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/watch/AbsServiceConnection.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.watch; 2 | 3 | import android.content.ComponentName; 4 | import android.content.ServiceConnection; 5 | import android.os.IBinder; 6 | public abstract class AbsServiceConnection implements ServiceConnection { 7 | 8 | // 当前绑定的状态 9 | public boolean mConnectedState = false; 10 | 11 | @Override 12 | public void onServiceConnected(ComponentName name, IBinder service) { 13 | mConnectedState = true; 14 | } 15 | 16 | @Override 17 | public void onServiceDisconnected(ComponentName name) { 18 | if (mConnectedState) { 19 | mConnectedState = false; 20 | onDisconnected(name); 21 | } 22 | } 23 | 24 | @Override 25 | public void onBindingDied(ComponentName name) { 26 | onServiceDisconnected(name); 27 | } 28 | 29 | public abstract void onDisconnected(ComponentName name); 30 | } 31 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/watch/JobSchedulerService.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.watch; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.job.JobParameters; 5 | import android.app.job.JobService; 6 | import android.os.Build; 7 | import android.util.Log; 8 | 9 | import com.sdk.keepbackground.work.DaemonEnv; 10 | 11 | /** 12 | * Android 5.0+ 使用的 JobScheduler. 13 | * 运行在 :watch 子进程中. 14 | * 15 | */ 16 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 17 | public class JobSchedulerService extends JobService { 18 | 19 | @Override 20 | public boolean onStartJob(JobParameters params) { 21 | Log.d("sj_keep", "JobSchedulerService onStartJob 启动。。。。"); 22 | DaemonEnv.startServiceSafely(JobSchedulerService.this, 23 | WatchDogService.class); 24 | return false; 25 | } 26 | 27 | @Override 28 | public boolean onStopJob(JobParameters params) { 29 | Log.d("sj_keep", "JobSchedulerService onStopJob 停止。。。。"); 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/watch/PlayMusicService.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.watch; 2 | 3 | import android.app.Service; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | import android.media.MediaPlayer; 9 | import android.os.IBinder; 10 | import android.util.Log; 11 | 12 | import com.sdk.keepbackground.work.DaemonEnv; 13 | import com.sdk.keepbackground.utils.ForegroundNotificationUtils; 14 | import com.sdk.keepbackground.R; 15 | 16 | /** 17 | * 后台播放无声音乐 18 | */ 19 | public class PlayMusicService extends Service { 20 | private boolean mNeedStop = false; //控制是否播放音频 21 | private MediaPlayer mMediaPlayer; 22 | private StopBroadcastReceiver stopBroadcastReceiver; 23 | 24 | @Override 25 | public IBinder onBind(Intent intent) { 26 | return null; 27 | } 28 | 29 | @Override 30 | public void onCreate() { 31 | super.onCreate(); 32 | ForegroundNotificationUtils.startForegroundNotification(this); 33 | startRegisterReceiver(); 34 | mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.no_notice); 35 | mMediaPlayer.setLooping(true); 36 | } 37 | 38 | @Override 39 | public int onStartCommand(Intent intent, int flags, int startId) { 40 | startPlayMusic(); 41 | return START_STICKY; 42 | } 43 | 44 | private void startPlayMusic(){ 45 | if (mMediaPlayer!=null && !mMediaPlayer.isPlaying() && !mNeedStop) { 46 | new Thread(new Runnable() { 47 | @Override 48 | public void run() { 49 | Log.d("sj_keep", "开始后台播放音乐"); 50 | mMediaPlayer.start(); 51 | } 52 | }).start(); 53 | } 54 | } 55 | 56 | private void stopPlayMusic() { 57 | if (mMediaPlayer != null) { 58 | Log.d("sj_keep", "关闭后台播放音乐"); 59 | mMediaPlayer.stop(); 60 | } 61 | } 62 | 63 | @Override 64 | public void onDestroy() { 65 | super.onDestroy(); 66 | stopPlayMusic(); 67 | Log.d("sj_keep", "----> stopPlayMusic ,停止服务"); 68 | // 重启自己 69 | if (!mNeedStop) { 70 | Log.d("sj_keep", "----> PlayMusic ,重启服务"); 71 | Intent intent = new Intent(getApplicationContext(), PlayMusicService.class); 72 | startService(intent); 73 | } 74 | } 75 | 76 | private void startRegisterReceiver(){ 77 | if (stopBroadcastReceiver == null){ 78 | stopBroadcastReceiver = new StopBroadcastReceiver(); 79 | IntentFilter intentFilter = new IntentFilter(); 80 | intentFilter.addAction(DaemonEnv.ACTION_CANCEL_JOB_ALARM_SUB); 81 | registerReceiver(stopBroadcastReceiver,intentFilter); 82 | } 83 | } 84 | 85 | private void startUnRegisterReceiver(){ 86 | if (stopBroadcastReceiver != null){ 87 | unregisterReceiver(stopBroadcastReceiver); 88 | stopBroadcastReceiver = null; 89 | } 90 | } 91 | 92 | /** 93 | * 停止自己 94 | */ 95 | private void stopService(){ 96 | mNeedStop = true; 97 | stopPlayMusic(); 98 | startUnRegisterReceiver(); 99 | stopSelf(); 100 | } 101 | 102 | class StopBroadcastReceiver extends BroadcastReceiver { 103 | 104 | @Override 105 | public void onReceive(Context context, Intent intent) { 106 | stopService(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/watch/WakeUpReceiver.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.watch; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | 8 | import com.sdk.keepbackground.work.DaemonEnv; 9 | 10 | 11 | public class WakeUpReceiver extends BroadcastReceiver { 12 | 13 | /** 14 | * 监听 8 种系统广播 : 15 | * CONNECTIVITY\_CHANGE, USER\_PRESENT, ACTION\_POWER\_CONNECTED, ACTION\_POWER\_DISCONNECTED, 16 | * BOOT\_COMPLETED, MEDIA\_MOUNTED, PACKAGE\_ADDED, PACKAGE\_REMOVED. 17 | * 在网络连接改变, 用户屏幕解锁, 电源连接 / 断开, 系统启动完成, 挂载 SD 卡, 安装 / 卸载软件包时拉起 Service. 18 | * Service 内部做了判断,若 Service 已在运行,不会重复启动. 19 | * 运行在:watch子进程中. 20 | */ 21 | @SuppressLint("UnsafeProtectedBroadcastReceiver") 22 | @Override 23 | public void onReceive(Context context, Intent intent) { 24 | DaemonEnv.startServiceSafely(context, WatchDogService.class); 25 | } 26 | 27 | public static class WakeUpAutoStartReceiver extends BroadcastReceiver { 28 | 29 | @SuppressLint("UnsafeProtectedBroadcastReceiver") 30 | @Override 31 | public void onReceive(Context context, Intent intent) { 32 | DaemonEnv.startServiceSafely(context,WatchDogService.class); 33 | } 34 | } 35 | 36 | public static class StartWatchReceiver extends BroadcastReceiver { 37 | 38 | @SuppressLint("UnsafeProtectedBroadcastReceiver") 39 | @Override 40 | public void onReceive(Context context, Intent intent) { 41 | WatchProcessPrefHelper.setIsStartSDaemon(context,true); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/watch/WatchDogService.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.watch; 2 | 3 | import android.app.AlarmManager; 4 | import android.app.PendingIntent; 5 | import android.app.Service; 6 | import android.app.job.JobInfo; 7 | import android.app.job.JobScheduler; 8 | import android.content.BroadcastReceiver; 9 | import android.content.ComponentName; 10 | import android.content.Context; 11 | import android.content.Intent; 12 | import android.content.IntentFilter; 13 | import android.content.pm.PackageManager; 14 | import android.os.Build; 15 | import android.os.Handler; 16 | import android.os.IBinder; 17 | import android.os.Messenger; 18 | import android.util.Log; 19 | 20 | import com.sdk.keepbackground.work.DaemonEnv; 21 | import com.sdk.keepbackground.utils.ForegroundNotificationUtils; 22 | 23 | 24 | 25 | 26 | public class WatchDogService extends Service { 27 | protected static final int HASH_CODE = 11222; 28 | // protected static Disposable mDisposable; 29 | protected static PendingIntent mPendingIntent; 30 | private StopBroadcastReceiver stopBroadcastReceiver; 31 | private boolean isCanStartWatchDog; 32 | 33 | /** 34 | * 服务绑定相关的操作 35 | */ 36 | private AbsServiceConnection mConnection = new AbsServiceConnection() { 37 | 38 | @Override 39 | public void onDisconnected(ComponentName name) { 40 | startBindWorkServices(); 41 | } 42 | }; 43 | 44 | private void startBindWorkServices(){ 45 | if (WatchProcessPrefHelper.getWorkService()!=null && isCanStartWatchDog) { 46 | DaemonEnv.startServiceMayBind(WatchDogService.this, WatchProcessPrefHelper.mWorkServiceClass, mConnection); 47 | DaemonEnv.startServiceSafely(WatchDogService.this, PlayMusicService.class); 48 | } 49 | } 50 | 51 | 52 | @Override 53 | public void onCreate() { 54 | super.onCreate(); 55 | isCanStartWatchDog = WatchProcessPrefHelper.getIsStartDaemon(this); 56 | if (!isCanStartWatchDog){ 57 | stopSelf(); 58 | } 59 | startRegisterReceiver(); 60 | ForegroundNotificationUtils.startForegroundNotification(this); 61 | } 62 | 63 | @Override 64 | public final int onStartCommand(Intent intent, int flags, int startId) { 65 | onStart(); 66 | return START_STICKY; 67 | } 68 | 69 | /** 70 | * 守护服务,运行在:watch子进程中 71 | */ 72 | protected final void onStart() { 73 | // if (mDisposable == null || mDisposable.isDisposed()) { 74 | if (mPendingIntent == null ) { 75 | //定时检查 AbsWorkService 是否在运行,如果不在运行就把它拉起来 Android 5.0+ 使用 JobScheduler,效果比 AlarmManager 好 76 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 77 | JobInfo.Builder builder = new JobInfo.Builder(HASH_CODE, 78 | new ComponentName(WatchDogService.this, JobSchedulerService.class)); 79 | builder.setPeriodic(DaemonEnv.getWakeUpInterval(DaemonEnv.MINIMAL_WAKE_UP_INTERVAL)); 80 | //Android 7.0+ 增加了一项针对 JobScheduler 的新限制,最小间隔只能是下面设定的数字 81 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 82 | builder.setPeriodic(JobInfo.getMinPeriodMillis(), JobInfo.getMinFlexMillis()); 83 | } 84 | builder.setPersisted(true); 85 | JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE); 86 | scheduler.schedule(builder.build()); 87 | } else { 88 | //Android 4.4- 使用 AlarmManager 89 | if(WatchProcessPrefHelper.getWorkService()!=null) { 90 | AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); 91 | Intent i = new Intent(WatchDogService.this, WatchProcessPrefHelper.mWorkServiceClass); 92 | mPendingIntent = PendingIntent.getService(WatchDogService.this, HASH_CODE, i, PendingIntent.FLAG_UPDATE_CURRENT); 93 | am.setRepeating(AlarmManager.RTC_WAKEUP, 94 | System.currentTimeMillis() + DaemonEnv.getWakeUpInterval(DaemonEnv.MINIMAL_WAKE_UP_INTERVAL), 95 | DaemonEnv.getWakeUpInterval(DaemonEnv.MINIMAL_WAKE_UP_INTERVAL), mPendingIntent); 96 | } 97 | } 98 | //使用定时 Observable,避免 Android 定制系统 JobScheduler / AlarmManager 唤醒间隔不稳定的情况 99 | /* mDisposable = Observable 100 | .interval(DaemonEnv.getWakeUpInterval(DaemonEnv.MINIMAL_WAKE_UP_INTERVAL), TimeUnit.MILLISECONDS) 101 | .subscribe(new Consumer