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