diff --git a/src/app/src/main/java/com/example/sleep/sensors/service/AliveJobService.java b/src/app/src/main/java/com/example/sleep/sensors/service/AliveJobService.java new file mode 100644 index 0000000..a05c5da --- /dev/null +++ b/src/app/src/main/java/com/example/sleep/sensors/service/AliveJobService.java @@ -0,0 +1,56 @@ +package com.example.sleep.service; + +import android.app.Service; +import android.app.job.JobParameters; +import android.app.job.JobService; +import android.content.Intent; +import android.os.Handler; +import android.os.Message; + +import com.example.sleep.MainScreen; +import com.example.sleep.utils.SystemUtils; + +/** + * 用于保活的JobService + */ +public class AliveJobService extends JobService { + private volatile static Service mKeepAliveService = null; + + public static boolean isJobServiceAlive() { + return mKeepAliveService != null; + } + + private static final int MESSAGE_ID_TASK = 0x01; + + /** + * 用来保活的Handler + */ + private Handler mHandler = new Handler(new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + // 如果应用已经被关闭,则重新启动MainActivity + if (!SystemUtils.isAPPALive(getApplicationContext(), "com.howard.sleephelper")) { + Intent intent = new Intent(getApplicationContext(), MainScreen.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + jobFinished((JobParameters) msg.obj, false); + return true; + } + }); + + @Override + public boolean onStartJob(JobParameters params) { + mKeepAliveService = this; + Message msg = Message.obtain(mHandler, MESSAGE_ID_TASK, params); + mHandler.sendMessage(msg); + return true; + } + + @Override + public boolean onStopJob(JobParameters params) { + // 移除保活的消息 + mHandler.removeMessages(MESSAGE_ID_TASK); + return false; + } +} diff --git a/src/app/src/main/java/com/example/sleep/sensors/service/DaemonService.java b/src/app/src/main/java/com/example/sleep/sensors/service/DaemonService.java new file mode 100644 index 0000000..ac6aefa --- /dev/null +++ b/src/app/src/main/java/com/example/sleep/sensors/service/DaemonService.java @@ -0,0 +1,73 @@ +package com.example.sleep.service; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Intent; +import android.os.Build; +import android.os.IBinder; +import android.support.annotation.Nullable; +import android.support.v4.app.NotificationCompat; + +import com.example.sleep.R; + +/** + * 用于显示通知的Service + */ +public class DaemonService extends Service { + public static final int NOTICE_ID = 100; + private NotificationManager manager; + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + /** + * 初始化获取channel manager与notification构造器 + */ + @Override + public void onCreate() { + super.onCreate(); + manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + Notification.Builder sleep = createNotification(); + manager.notify(NOTICE_ID, sleep.build()); + } + + /** + * 创建通知及通知频道 + * + * @return 通知构造器 + */ + Notification.Builder createNotification() { + Notification.Builder builder = new Notification.Builder(this); + builder.setSmallIcon(R.drawable.sleep_1) + .setContentTitle("睡眠助手") + .setContentText("正在记录您的睡眠...") + .setOngoing(true) + .setDefaults(NotificationCompat.DEFAULT_ALL); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel("1", "睡眠记录", NotificationManager.IMPORTANCE_LOW); + channel.canBypassDnd(); + channel.getAudioAttributes(); + channel.setBypassDnd(true); + channel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC); + manager.createNotificationChannel(channel); + builder.setChannelId("1"); + } + return builder; + } + + @Override + public void onDestroy() { + super.onDestroy(); + // 取消通知 + NotificationManager mManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + if (mManager != null) { + mManager.cancel(NOTICE_ID); + } + } +} diff --git a/src/app/src/main/java/com/example/sleep/sensors/service/GoSleepService.java b/src/app/src/main/java/com/example/sleep/sensors/service/GoSleepService.java new file mode 100644 index 0000000..8e777f3 --- /dev/null +++ b/src/app/src/main/java/com/example/sleep/sensors/service/GoSleepService.java @@ -0,0 +1,152 @@ +package com.example.sleep.service; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Intent; +import android.os.Build; +import android.os.IBinder; +import android.support.v4.app.NotificationCompat; + +import com.example.sleep.R; +import com.example.sleep.database.GetRecord; + +import java.util.Calendar; +import java.util.Timer; +import java.util.TimerTask; + +import static com.example.sleep.database.GetRecord.getRecord; + +public class GoSleepService extends Service { + NotificationManager mManager; + Notification.Builder sleep; + + GetRecord myGet; + + int hour; + int min; + boolean ifSleep = false; + boolean notification_on = false; + + Timer timer; + + /** + * 初始化,顺便获取当天是否已经睡觉记录,如已睡觉则不再提醒。 + */ + @Override + public void onCreate() { + super.onCreate(); + // 获取睡眠记录 + myGet = getRecord(); + // 获取通知管理器 + mManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + // 创建睡眠通知 + sleep = createNotification(); + // 休眠3秒,避免立即执行任务 + try { + Thread.sleep(3000); + } catch (InterruptedException ignored) { + } + // 获取提醒时间 + String remind = myGet.getRemind(); + String[] res = remind.split(":"); + hour = Integer.parseInt(res[0]); + min = Integer.parseInt(res[1]); + // 检查当天是否已经睡觉 + ifSleep = ifSleepToday(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // 创建定时任务 + TimerTask timerTask = new TimerTask() { + @Override + public void run() { + // 如果未睡觉且通知未显示,则检查是否到达提醒时间 + if (!ifSleep && !notification_on) { + Calendar now = Calendar.getInstance(); + if ((now.get(Calendar.HOUR_OF_DAY)) >= hour && (now.get(Calendar.MINUTE)) >= min) { + // 显示睡眠提醒通知 + mManager.notify(2, sleep.build()); + notification_on = true; + } + } + } + }; + // 创建并执行定时任务,每30秒检查一次 + timer = new Timer(); + timer.schedule(timerTask, 0, 30000); + return START_STICKY; + } + + // 此为开机自启成功与否测试 + Notification.Builder createMyNotification() { + Notification.Builder builder = new Notification.Builder(this); + builder + .setContentTitle("睡眠助手") + .setDefaults(NotificationCompat.DEFAULT_ALL); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // 创建通知频道 + NotificationChannel channel = new NotificationChannel("3", "睡眠记录", NotificationManager.IMPORTANCE_DEFAULT); + channel.canBypassDnd(); + channel.getAudioAttributes(); + channel.setBypassDnd(true); + channel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC); + mManager.createNotificationChannel(channel); + builder.setChannelId("3"); + } + return builder; + } + + /** + * 创建通知及通知频道 + * + * @return 通知构造器 + */ + Notification.Builder createNotification() { + Notification.Builder builder = new Notification.Builder(this); + builder.setSmallIcon(R.drawable.sleep_1) + .setContentTitle("睡眠助手") + .setContentText("小助手提醒您该睡觉啦") + .setOngoing(true) + .setDefaults(NotificationCompat.DEFAULT_ALL).setOngoing(true); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // 创建通知频道 + NotificationChannel channel = new NotificationChannel("2", "睡眠提醒", NotificationManager.IMPORTANCE_DEFAULT); + channel.canBypassDnd(); + channel.getAudioAttributes(); + channel.setBypassDnd(true); + channel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC); + mManager.createNotificationChannel(channel); + builder.setChannelId("2"); + } + return builder; + } + + /** + * 今日是否睡觉 + * + * @return 已睡觉,未睡觉 + **/ + public boolean ifSleepToday() { + Calendar calendar = Calendar.getInstance(); + return !getRecord().queryByDate(((calendar.get(Calendar.MONTH) + 1) + "-" + calendar.get(Calendar.DAY_OF_MONTH))).isEmpty(); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onDestroy() { + // 取消定时任务和通知 + timer.cancel(); + mManager.cancel(2); + super.onDestroy(); + } +} + diff --git a/src/app/src/main/java/com/example/sleep/sensors/service/GrayService.java b/src/app/src/main/java/com/example/sleep/sensors/service/GrayService.java new file mode 100644 index 0000000..9149e1a --- /dev/null +++ b/src/app/src/main/java/com/example/sleep/sensors/service/GrayService.java @@ -0,0 +1,37 @@ +package com.example.sleep.service; + +import android.app.Notification; +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class GrayService extends Service { + + private final static int GRAY_SERVICE_ID = -1001; + + @Override + public void onCreate() { + super.onCreate(); + } + + /** + * 利用曾经的漏洞保活 + */ + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + startForeground(GRAY_SERVICE_ID, new Notification()); + //stopForeground(true); + stopSelf(); + return super.onStartCommand(intent, flags, startId); + } + + @Override + public IBinder onBind(Intent intent) { + throw new UnsupportedOperationException("Not yet implemented"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } +} diff --git a/src/app/src/main/java/com/example/sleep/sensors/service/SensorService.java b/src/app/src/main/java/com/example/sleep/sensors/service/SensorService.java new file mode 100644 index 0000000..a1f571b --- /dev/null +++ b/src/app/src/main/java/com/example/sleep/sensors/service/SensorService.java @@ -0,0 +1,170 @@ +package com.example.sleep.service; + +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Messenger; +import android.os.PowerManager; +import android.support.annotation.NonNull; +import android.util.Log; + +import com.example.sleep.database.GetRecord; +import com.example.sleep.database.RecordBean; +import com.example.sleep.sensors.Sensors; +import com.shihoo.daemon.AbsWorkService; + +import java.util.Calendar; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Action; +import io.reactivex.functions.Consumer; + +import static android.content.ContentValues.TAG; +import static com.example.sleep.database.GetRecord.getRecord; + +public class SensorService extends AbsWorkService { + + private boolean isShouldStopService; // 标记是否应该停止服务 + private Disposable mDisposable; // 用于取消任务的订阅 + private Sensors sensor; // 传感器对象 + private RecordBean mRecord; // 记录数据对象 + private GetRecord mGetRecord; // 获取记录数据的对象 + + private boolean restart; // 标记是否需要重新启动 + private long startTime; // 任务开始时间 + PowerManager.WakeLock m_wake; // 电池锁对象 + + /** + * 是否任务完成, 不再需要服务运行? + * + * @return 应当停止服务, true; 应当启动服务, false; 无法判断, 什么也不做, null. + */ + @Override + public Boolean shouldStopService(Intent intent, int flags, int startId) { + return isShouldStopService; + } + + /** + * 任务是否正在运行? + * + * @return 任务正在运行, true; 任务当前不在运行, false; 无法判断, 什么也不做, null. + */ + @Override + public Boolean isWorkRunning(Intent intent, int flags, int startId) { + // 若还没有取消订阅, 就说明任务仍在运行. + return mDisposable != null && !mDisposable.isDisposed(); + } + + @NonNull + @Override + public IBinder onBindService(Intent intent, Void v) { + // 此处必须有返回,否则绑定无回调 + return new Messenger(new Handler()).getBinder(); + } + + /** + * 当服务被杀之后调用的方法 + * + * @param rootIntent (继承的,么的用) + */ + @Override + public void onServiceKilled(Intent rootIntent) { + if (sensor != null) { + Log.e(TAG, "i die"); + sensor.stopSensor(); // 停止传感器 + } + } + + /** + * 当这个service正常停止后的方法(我们用的部分比较简单,暂时不需要继承方法提供的变量) + */ + @Override + public void stopWork(Intent intent, int flags, int startId) { + + isShouldStopService = true; // 设置停止服务标志为true + + if (sensor != null) { + int[] details = sensor.stopSensor(); // 停止传感器并获取详细信息 + int deepTime = details[0]; + int swallowTime = details[1]; + int awakeTime = details[2]; + Calendar calendar = Calendar.getInstance(); + if (mRecord != null) { + mGetRecord.finalUpdate(mRecord, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), + calendar.getTimeInMillis() - startTime, deepTime, swallowTime, awakeTime); + } + } + // 取消对任务的订阅 + if (mDisposable != null && !mDisposable.isDisposed()) { + mDisposable.dispose(); + } + } + + /** + * 我们尝试使用电池锁需要在初始化时仅一次调用电池锁,所以重写了这个start方法 + * 考虑到兼容性和电量消耗在提交版本中未启用 + */ + void startService(Intent intent, int flags, int startId) { + // 若还没有取消订阅,说明任务仍在运行,为防止重复启动,直接 return + Boolean workRunning = isWorkRunning(intent, flags, startId); + if (workRunning != null && workRunning) { + return; + } + Log.d(TAG, " use this start power and " + SensorService.class.getName()); + startWork(intent, flags, startId); + } + + /** + * 当服务被杀之后重新开始的初始化部分,会被调用多次,进行了传感器的初始化以及注册 + */ + @Override + public void startWork(Intent intent, int flags, int startId) { + Calendar calendar = Calendar.getInstance(); + startTime = calendar.getTimeInMillis(); + // 注意:若启用mJobManager则需要重新调整reStart内容 + mGetRecord = getRecord(); + mRecord = mGetRecord.getLatestRecord(); + if (mRecord != null) { + restart = !mRecord.getValid(); + } + if (restart) { + mRecord = mGetRecord.getLatestRecord(); + Log.e(TAG, "i stop"); + sensor = new Sensors(this, mRecord); // 初始化传感器 + } else { + mRecord = mGetRecord.insertData((calendar.get(Calendar.MONTH) + 1) + "-" + + calendar.get(Calendar.DATE), + String.format(Locale.getDefault(), "%02d:%02d", + calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE))); + sensor = new Sensors(this, mRecord); // 初始化传感器 + } + mDisposable = Observable + .interval(3, TimeUnit.SECONDS) + // 取消任务时取消定时唤醒 + .doOnDispose(new Action() { + @Override + public void run() { + } + }) + .subscribe(new Consumer() { + @Override + public void accept(Long aLong) { + } + }); + } + + /** + * 以后可能会用到的电池锁放开的方法 + */ + private void releaseWakeLock() { + if (null != m_wake && m_wake.isHeld()) { + Log.d(TAG, "call releaseWakeLock"); + m_wake.release(); + m_wake = null; + } + } +} +