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