/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.notification; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.service.notification.Condition; import android.service.notification.IConditionProvider; import android.service.notification.ZenModeConfig; import android.text.format.DateUtils; import android.util.Log; import android.util.Slog; import com.android.server.notification.NotificationManagerService.DumpFilter; import java.io.PrintWriter; /** Built-in zen condition provider for simple time-based conditions */ public class CountdownConditionProvider extends SystemConditionProviderService { private static final String TAG = "ConditionProviders.CCP"; private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG); public static final ComponentName COMPONENT = new ComponentName("android", CountdownConditionProvider.class.getName()); private static final String ACTION = CountdownConditionProvider.class.getName(); private static final int REQUEST_CODE = 100; private static final String EXTRA_CONDITION_ID = "condition_id"; private final Context mContext = this; private final Receiver mReceiver = new Receiver(); private boolean mConnected; private long mTime; public CountdownConditionProvider() { if (DEBUG) Slog.d(TAG, "new CountdownConditionProvider()"); } @Override public ComponentName getComponent() { return COMPONENT; } @Override public boolean isValidConditionId(Uri id) { return ZenModeConfig.isValidCountdownConditionId(id); } @Override public void attachBase(Context base) { attachBaseContext(base); } @Override public void onBootComplete() { // noop } @Override public IConditionProvider asInterface() { return (IConditionProvider) onBind(null); } @Override public void dump(PrintWriter pw, DumpFilter filter) { pw.println(" CountdownConditionProvider:"); pw.print(" mConnected="); pw.println(mConnected); pw.print(" mTime="); pw.println(mTime); } @Override public void onConnected() { if (DEBUG) Slog.d(TAG, "onConnected"); mContext.registerReceiver(mReceiver, new IntentFilter(ACTION)); mConnected = true; } @Override public void onDestroy() { super.onDestroy(); if (DEBUG) Slog.d(TAG, "onDestroy"); if (mConnected) { mContext.unregisterReceiver(mReceiver); } mConnected = false; } @Override public void onSubscribe(Uri conditionId) { if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId); mTime = ZenModeConfig.tryParseCountdownConditionId(conditionId); final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); final Intent intent = new Intent(ACTION).putExtra(EXTRA_CONDITION_ID, conditionId) .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT); alarms.cancel(pendingIntent); if (mTime > 0) { final long now = System.currentTimeMillis(); final CharSequence span = DateUtils.getRelativeTimeSpanString(mTime, now, DateUtils.MINUTE_IN_MILLIS); if (mTime <= now) { // in the past, already false notifyCondition(newCondition(mTime, Condition.STATE_FALSE)); } else { // in the future, set an alarm alarms.setExact(AlarmManager.RTC_WAKEUP, mTime, pendingIntent); } if (DEBUG) Slog.d(TAG, String.format( "%s %s for %s, %s in the future (%s), now=%s", (mTime <= now ? "Not scheduling" : "Scheduling"), ACTION, ts(mTime), mTime - now, span, ts(now))); } } @Override public void onUnsubscribe(Uri conditionId) { // noop } private final class Receiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (ACTION.equals(intent.getAction())) { final Uri conditionId = intent.getParcelableExtra(EXTRA_CONDITION_ID); final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId); if (DEBUG) Slog.d(TAG, "Countdown condition fired: " + conditionId); if (time > 0) { notifyCondition(newCondition(time, Condition.STATE_FALSE)); } } } } private static final Condition newCondition(long time, int state) { return new Condition(ZenModeConfig.toCountdownConditionId(time), "", "", "", 0, state,Condition.FLAG_RELEVANT_NOW); } public static String tryParseDescription(Uri conditionUri) { final long time = ZenModeConfig.tryParseCountdownConditionId(conditionUri); if (time == 0) return null; final long now = System.currentTimeMillis(); final CharSequence span = DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS); return String.format("Scheduled for %s, %s in the future (%s), now=%s", ts(time), time - now, span, ts(now)); } }