CountdownConditionProvider.java revision 4db0d98b42a723f2e16c6595e85e866fd26c6d98
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.Log;
33import android.util.Slog;
34
35import com.android.server.notification.NotificationManagerService.DumpFilter;
36
37import java.io.PrintWriter;
38import java.util.Date;
39
40/** Built-in zen condition provider for simple time-based conditions */
41public 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