Home | History | Annotate | Download | only in notification
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.notification;
     18 
     19 import android.app.AlarmManager;
     20 import android.app.PendingIntent;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ComponentName;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.net.Uri;
     27 import android.service.notification.Condition;
     28 import android.service.notification.ConditionProviderService;
     29 import android.service.notification.IConditionProvider;
     30 import android.service.notification.ZenModeConfig;
     31 import android.text.format.DateUtils;
     32 import android.util.Log;
     33 import android.util.Slog;
     34 
     35 import com.android.server.notification.NotificationManagerService.DumpFilter;
     36 
     37 import java.io.PrintWriter;
     38 import java.util.Date;
     39 
     40 /** Built-in zen condition provider for simple time-based conditions */
     41 public class CountdownConditionProvider extends ConditionProviderService {
     42     private static final String TAG = "CountdownConditions";
     43     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     44 
     45     public static final ComponentName COMPONENT =
     46             new ComponentName("android", CountdownConditionProvider.class.getName());
     47 
     48     private static final String ACTION = CountdownConditionProvider.class.getName();
     49     private static final int REQUEST_CODE = 100;
     50     private static final String EXTRA_CONDITION_ID = "condition_id";
     51 
     52     private final Context mContext = this;
     53     private final Receiver mReceiver = new Receiver();
     54 
     55     private boolean mConnected;
     56     private long mTime;
     57 
     58     public CountdownConditionProvider() {
     59         if (DEBUG) Slog.d(TAG, "new CountdownConditionProvider()");
     60     }
     61 
     62     public void dump(PrintWriter pw, DumpFilter filter) {
     63         pw.println("    CountdownConditionProvider:");
     64         pw.print("      mConnected="); pw.println(mConnected);
     65         pw.print("      mTime="); pw.println(mTime);
     66     }
     67 
     68     @Override
     69     public void onConnected() {
     70         if (DEBUG) Slog.d(TAG, "onConnected");
     71         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION));
     72         mConnected = true;
     73     }
     74 
     75     @Override
     76     public void onDestroy() {
     77         super.onDestroy();
     78         if (DEBUG) Slog.d(TAG, "onDestroy");
     79         if (mConnected) {
     80             mContext.unregisterReceiver(mReceiver);
     81         }
     82         mConnected = false;
     83     }
     84 
     85     @Override
     86     public void onRequestConditions(int relevance) {
     87         // by convention
     88     }
     89 
     90     @Override
     91     public void onSubscribe(Uri conditionId) {
     92         if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
     93         mTime = ZenModeConfig.tryParseCountdownConditionId(conditionId);
     94         final AlarmManager alarms = (AlarmManager)
     95                 mContext.getSystemService(Context.ALARM_SERVICE);
     96         final Intent intent = new Intent(ACTION).putExtra(EXTRA_CONDITION_ID, conditionId)
     97                 .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
     98         final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
     99                 intent, PendingIntent.FLAG_UPDATE_CURRENT);
    100         alarms.cancel(pendingIntent);
    101         if (mTime > 0) {
    102             final long now = System.currentTimeMillis();
    103             final CharSequence span =
    104                     DateUtils.getRelativeTimeSpanString(mTime, now, DateUtils.MINUTE_IN_MILLIS);
    105             if (mTime <= now) {
    106                 // in the past, already false
    107                 notifyCondition(newCondition(mTime, Condition.STATE_FALSE));
    108             } else {
    109                 // in the future, set an alarm
    110                 alarms.setExact(AlarmManager.RTC_WAKEUP, mTime, pendingIntent);
    111             }
    112             if (DEBUG) Slog.d(TAG, String.format(
    113                     "%s %s for %s, %s in the future (%s), now=%s",
    114                     (mTime <= now ? "Not scheduling" : "Scheduling"),
    115                     ACTION, ts(mTime), mTime - now, span, ts(now)));
    116         }
    117     }
    118 
    119     @Override
    120     public void onUnsubscribe(Uri conditionId) {
    121         // noop
    122     }
    123 
    124     private final class Receiver extends BroadcastReceiver {
    125         @Override
    126         public void onReceive(Context context, Intent intent) {
    127             if (ACTION.equals(intent.getAction())) {
    128                 final Uri conditionId = intent.getParcelableExtra(EXTRA_CONDITION_ID);
    129                 final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
    130                 if (DEBUG) Slog.d(TAG, "Countdown condition fired: " + conditionId);
    131                 if (time > 0) {
    132                     notifyCondition(newCondition(time, Condition.STATE_FALSE));
    133                 }
    134             }
    135         }
    136     }
    137 
    138     private static final Condition newCondition(long time, int state) {
    139         return new Condition(ZenModeConfig.toCountdownConditionId(time),
    140                 "", "", "", 0, state,Condition.FLAG_RELEVANT_NOW);
    141     }
    142 
    143     public static String tryParseDescription(Uri conditionUri) {
    144         final long time = ZenModeConfig.tryParseCountdownConditionId(conditionUri);
    145         if (time == 0) return null;
    146         final long now = System.currentTimeMillis();
    147         final CharSequence span =
    148                 DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS);
    149         return String.format("Scheduled for %s, %s in the future (%s), now=%s",
    150                 ts(time), time - now, span, ts(now));
    151     }
    152 
    153     private static String ts(long time) {
    154         return new Date(time) + " (" + time + ")";
    155     }
    156 
    157     public void attachBase(Context base) {
    158         attachBaseContext(base);
    159     }
    160 
    161     public IConditionProvider asInterface() {
    162         return (IConditionProvider) onBind(null);
    163     }
    164 }
    165