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.IConditionProvider;
     29 import android.service.notification.ZenModeConfig;
     30 import android.text.format.DateUtils;
     31 import android.util.Log;
     32 import android.util.Slog;
     33 
     34 import com.android.server.notification.NotificationManagerService.DumpFilter;
     35 
     36 import java.io.PrintWriter;
     37 
     38 /** Built-in zen condition provider for simple time-based conditions */
     39 public class CountdownConditionProvider extends SystemConditionProviderService {
     40     private static final String TAG = "ConditionProviders.CCP";
     41     private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG);
     42 
     43     public static final ComponentName COMPONENT =
     44             new ComponentName("android", CountdownConditionProvider.class.getName());
     45 
     46     private static final String ACTION = CountdownConditionProvider.class.getName();
     47     private static final int REQUEST_CODE = 100;
     48     private static final String EXTRA_CONDITION_ID = "condition_id";
     49 
     50     private final Context mContext = this;
     51     private final Receiver mReceiver = new Receiver();
     52 
     53     private boolean mConnected;
     54     private long mTime;
     55     private boolean mIsAlarm;
     56 
     57     public CountdownConditionProvider() {
     58         if (DEBUG) Slog.d(TAG, "new CountdownConditionProvider()");
     59     }
     60 
     61     @Override
     62     public ComponentName getComponent() {
     63         return COMPONENT;
     64     }
     65 
     66     @Override
     67     public boolean isValidConditionId(Uri id) {
     68         return ZenModeConfig.isValidCountdownConditionId(id);
     69     }
     70 
     71     @Override
     72     public void attachBase(Context base) {
     73         attachBaseContext(base);
     74     }
     75 
     76     @Override
     77     public void onBootComplete() {
     78         // noop
     79     }
     80 
     81     @Override
     82     public IConditionProvider asInterface() {
     83         return (IConditionProvider) onBind(null);
     84     }
     85 
     86     @Override
     87     public void dump(PrintWriter pw, DumpFilter filter) {
     88         pw.println("    CountdownConditionProvider:");
     89         pw.print("      mConnected="); pw.println(mConnected);
     90         pw.print("      mTime="); pw.println(mTime);
     91     }
     92 
     93     @Override
     94     public void onConnected() {
     95         if (DEBUG) Slog.d(TAG, "onConnected");
     96         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION));
     97         mConnected = true;
     98     }
     99 
    100     @Override
    101     public void onDestroy() {
    102         super.onDestroy();
    103         if (DEBUG) Slog.d(TAG, "onDestroy");
    104         if (mConnected) {
    105             mContext.unregisterReceiver(mReceiver);
    106         }
    107         mConnected = false;
    108     }
    109 
    110     @Override
    111     public void onSubscribe(Uri conditionId) {
    112         if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
    113         mTime = ZenModeConfig.tryParseCountdownConditionId(conditionId);
    114         mIsAlarm = ZenModeConfig.isValidCountdownToAlarmConditionId(conditionId);
    115         final AlarmManager alarms = (AlarmManager)
    116                 mContext.getSystemService(Context.ALARM_SERVICE);
    117         final Intent intent = new Intent(ACTION)
    118                 .putExtra(EXTRA_CONDITION_ID, conditionId)
    119                 .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    120         final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
    121                 intent, PendingIntent.FLAG_UPDATE_CURRENT);
    122         alarms.cancel(pendingIntent);
    123         if (mTime > 0) {
    124             final long now = System.currentTimeMillis();
    125             final CharSequence span =
    126                     DateUtils.getRelativeTimeSpanString(mTime, now, DateUtils.MINUTE_IN_MILLIS);
    127             if (mTime <= now) {
    128                 // in the past, already false
    129                 notifyCondition(newCondition(mTime, mIsAlarm, Condition.STATE_FALSE));
    130             } else {
    131                 // in the future, set an alarm
    132                 alarms.setExact(AlarmManager.RTC_WAKEUP, mTime, pendingIntent);
    133             }
    134             if (DEBUG) Slog.d(TAG, String.format(
    135                     "%s %s for %s, %s in the future (%s), now=%s",
    136                     (mTime <= now ? "Not scheduling" : "Scheduling"),
    137                     ACTION, ts(mTime), mTime - now, span, ts(now)));
    138         }
    139     }
    140 
    141     @Override
    142     public void onUnsubscribe(Uri conditionId) {
    143         // noop
    144     }
    145 
    146     private final class Receiver extends BroadcastReceiver {
    147         @Override
    148         public void onReceive(Context context, Intent intent) {
    149             if (ACTION.equals(intent.getAction())) {
    150                 final Uri conditionId = intent.getParcelableExtra(EXTRA_CONDITION_ID);
    151                 final boolean alarm = ZenModeConfig.isValidCountdownToAlarmConditionId(conditionId);
    152                 final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
    153                 if (DEBUG) Slog.d(TAG, "Countdown condition fired: " + conditionId);
    154                 if (time > 0) {
    155                     notifyCondition(newCondition(time, alarm, Condition.STATE_FALSE));
    156                 }
    157             }
    158         }
    159     }
    160 
    161     private static final Condition newCondition(long time, boolean alarm, int state) {
    162         return new Condition(ZenModeConfig.toCountdownConditionId(time, alarm),
    163                 "", "", "", 0, state,Condition.FLAG_RELEVANT_NOW);
    164     }
    165 
    166     public static String tryParseDescription(Uri conditionUri) {
    167         final long time = ZenModeConfig.tryParseCountdownConditionId(conditionUri);
    168         if (time == 0) return null;
    169         final long now = System.currentTimeMillis();
    170         final CharSequence span =
    171                 DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS);
    172         return String.format("Scheduled for %s, %s in the future (%s), now=%s",
    173                 ts(time), time - now, span, ts(now));
    174     }
    175 
    176 }
    177