text: /*
   * 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;
      private boolean mIsAlarm;
  
      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);
          mIsAlarm = ZenModeConfig.isValidCountdownToAlarmConditionId(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, mIsAlarm, 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 boolean alarm = ZenModeConfig.isValidCountdownToAlarmConditionId(conditionId);
                  final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
                  if (DEBUG) Slog.d(TAG, "Countdown condition fired: " + conditionId);
                  if (time > 0) {
                      notifyCondition(newCondition(time, alarm, Condition.STATE_FALSE));
                  }
              }
          }
      }
  
      private static final Condition newCondition(long time, boolean alarm, int state) {
          return new Condition(ZenModeConfig.toCountdownConditionId(time, alarm),
                  "", "", "", 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));
      }
  
  }
  