Android笔记—仿绿色守护嗜睡模式通知


资料来源如下

  • 第一行代码(第二版)
  • Android Training Notifying the User
  • Android API 通知
  • 【Android】状态栏通知Notification、NotificationManager详解

编程环境

  • Android Studio 2.2.3

导语

  • OneTapDoze遇到的第一个难题,顺带记录Android 通知相关内容

最终效果

  • 绿色守护进入doze模式后,会在通知栏创建一个计时通知 如下图
    Screenshot_20170307-231118.png

  • 退出doze模式后,会指示进入doze和退出doze的时间段。
    Screenshot_20170307-231132.png

  • 我们要仿照的样式就是这样,进入退出doze,对应创建通知的代码都在doze模式改变的Broadcast中。

基础部分

创建通知

  • 行文前:
    Android每次版本几乎都会有通知API的改动,不同版本之间通知兼容性很是问题,为此我们使用support_v7 库中的NotificationCompat.Builder代替Notification.Builder二者使用方式相同

  • 涉及到的两个类

    • NotificationManager
    • Notification (support库中对应NotificationCompat)
  • NotificationManager

    • 状态栏通知的管理类,负责发通知、清除通知等
    • NotificationManager 是一个系统Service,必须通过 getSystemService()方法来获取
  • Notification

    • 具体的状态栏通知对象,可以设置icon、文字、提示声音、振动等参数
    • 一个Builder对象至少包含三个方面
      • 一个小图标,通过setSmallIcon()方法设置。
      • 通知标题,通过setContentTitle()方法设置。
      • 详细文本,通过setContentText()方法设置。
  • 简单示例

    • 创建通知构建器
      代码

      1
      2
      3
      4
      5
      6
      Notification notification = new NotificationCompat.Builder(this)
      .setContentTitle("这是通知标题")
      .setContentText("这是通知内容")
      //这里使用的是应用图标,一般没人这么干,就是为了方便
      .setSmallIcon(R.mipmap.ic_launcher)
      .build();
    • 发布通知

      • 获得NotificationManager的实例
      • 使用notify()方法发布通知。在调用notify()方法 指定通知的ID,(ID用于通知更新) 加载Notification实例
      1
      2
      3
      4
      int mNotificationId = 001;

      NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
      manager.notify(mNotificationId, notification);
  • 效果如下

    • android 7.0 / 6.0 /4.0 通过
    • ScreenShot_20170202173430.png

更新通知

  • 可以创建一个全新的NotificationCompat对象,也可以在原NotificationCompat对象基础上修改,最后只要 .notify 方法中对应同一个 mNotificationId ,系统就会自动更新已有通知,代码不在累赘。

  • 基础部分到此,足矣

正题

  • 实现时间段的显示,肯定会保存系统进入doze 退出doze对应的时间点。再计算出中间经历的时间。

提取系统时间

  • java.util.Date
    这里用到了java中的 时间类型 java.util.Date
    java.util.Date 是java中常用时间类型,可以被SimpleDateFormat格式化format() 指定输出的时间格式
    比如我们需要 小时:分:秒
    SimpleDateFormat scanf = new SimpleDateFormat("HH:mm:ss");
    scanf.format(java.util.Date) 即可。

  • 提前当前系统时间

    1
    2
    java.util.Date time_after = null;
    time_after = new java.util.Date(System.currentTimeMillis());
  • 计算时间差
    理论上是两个时间相减再格式化输出即可,可惜没成,原因还没找到。于是土办法:

    • 调用 .getTime() 方法,将 java.util.Date 转化为毫秒计时(long)
    • 取两者差值
    • 转化为 时间长短 字符串返回,
  • 代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     size = (time_after.getTime() - time_befor.getTime());
    string = scanf.format(time_befor) + "-" + scanf.format(time_after) + "="+ trform(size);

    String trform(long size) {
    long day, hour, min, secone;

    day = size / (1000 * 60 * 60 * 24); //以天数为单位取整
    hour = (size / (60 * 60 * 1000) - day * 24); //以小时为单位取整
    min = ((size / (60 * 1000)) - day * 24 * 60 - hour * 60); //以分钟为单位取整
    secone = (size / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);

    if (day != 0) {
    return day + "d" + hour + "h" + min + "m" + secone + "s";
    } else if (hour != 0) {
    return hour + "h" + min + "m" + secone + "s";
    } else if (min != 0) {
    return min + "m" + secone + "s";
    } else {
    return secone + "s";
    }
    }

通知扩展布局

  • 在如下图片中,可以发现,通知在发出后,只默认显示最近一次的时间记录,其他时间记录可以在此条通知上下滑查看。明显与基础部分不同。
    Screenshot_20170307-231132.png

  • 此处应用了 Builder.setStyle() 拓展布局,顾名思义,这是用于拓展通知显示范围


使用扩展布局

  • 使用拓展布局
    • 构建一个 inboxStyle 对象
    • .addLine方法 添加 String inboxStyle 行
    • 使用 Builder.setStyle( inboxStyle )加载到通知
    • 等待通知发布即可。
  • 简单例程如下
    1
    2
    3
    4
    5
    6
    //创建 inboxStyle
    NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
    //添加 String 行 ,前提是 我已经创建了一个 String
    inboxStyle.addLine(string);
    // 在notification 设置 inboxStyle 在一堆 build 里面
    .setStyle(inboxStyle)
  • 清除原有内容,发布新的 inboxStyle 只需要 inboxStyle = new NotificationCompat.InboxStyle();

Doze模式改变广播

  • 对应 ACTION_DEVICE_IDLE_MODE_CHANGED

  • 坑:只能动态注册,静态注册无效,具体代码中powermanger.ACTION_DEVICE_IDLE_MODE_CHANGED 需要实例化 PowerManger ,而 PowerManger 又是与 Activity绑定,所以只有应用保持后台存活时才会进入广播。
  • 例程如下
    1
    2
    3
    4
    5
    6
    7
    8
    //实例化 PowerManager
    PowerManager powermanger = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
    //动态注册广播
    intentFilter = new IntentFilter();
    intentFilter.addAction(powermanger.ACTION_DEVICE_IDLE_MODE_CHANGED);
    //idlemodechage 是 BroadcastReceiver
    idlemodechage = new IdleModeChange();
    registerReceiver(idlemodechage, intentFilter);