Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f4eb4014 authored by Raj Yengisetty's avatar Raj Yengisetty Committed by Rajesh Yengisetty
Browse files

DeskClock: re-write delayAlarm for intercepting alarms when in-call

While the previous clean-up was necessary, it no longer meets the spec.
This feature needs to trigger the alarm immediately after the call finishes.

Change-Id: I71107277926c891f79b1e46fb27878994ac15ce0
(cherry picked from commit 544c5e83)
parent bac9a8e5
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -104,6 +104,12 @@
                  android:exported="false">
        </receiver>

        <receiver android:name=".alarms.PhoneStateReceiver">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE"/>
            </intent-filter>
        </receiver>

        <service android:name=".alarms.AlarmService"
                 android:exported="false">
        </service>
+78 −75
Original line number Diff line number Diff line
@@ -44,7 +44,9 @@ import com.android.deskclock.provider.Alarm;
import com.android.deskclock.provider.AlarmInstance;

import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * This class handles all the state changes for alarm instances. You need to
@@ -120,6 +122,9 @@ public final class AlarmStateManager extends BroadcastReceiver {
    public static final String ALARM_SNOOZE_TAG = "SNOOZE_TAG";
    public static final String ALARM_DELETE_TAG = "DELETE_TAG";

    // key to to retrieve pending alarm set
    public static final String ALARM_PENDING_ALARM_KEY = "pending.alarm.key";

    // Intent category tag used when schedule state change intents in alarm manager.
    private static final String ALARM_MANAGER_TAG = "ALARM_MANAGER";

@@ -140,6 +145,15 @@ public final class AlarmStateManager extends BroadcastReceiver {

    public static void updateGlobalIntentId(Context context) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);

        // If there are any pending alarms, do not update the global id - pending alarms
        // will be ignored by the receivers when the user tries to dismiss or snooze
        Set<String> alarms = prefs.getStringSet(AlarmStateManager.ALARM_PENDING_ALARM_KEY,
                new HashSet<String>());
        if (alarms.size() > 0) {
            return;
        }

        int globalId = prefs.getInt(ALARM_GLOBAL_ID_EXTRA, -1) + 1;
        prefs.edit().putInt(ALARM_GLOBAL_ID_EXTRA, globalId).commit();
    }
@@ -679,7 +693,7 @@ public final class AlarmStateManager extends BroadcastReceiver {
     * @param instance to change state on
     * @param state to change to
     */
    public void setAlarmState(Context context, AlarmInstance instance, int state) {
    public static void setAlarmState(Context context, AlarmInstance instance, int state) {
        switch(state) {
            case AlarmInstance.SILENT_STATE:
                setSilentState(context, instance);
@@ -736,6 +750,27 @@ public final class AlarmStateManager extends BroadcastReceiver {
        final String action = intent.getAction();
        LogUtils.v("AlarmStateManager received intent " + intent);
        if (CHANGE_STATE_ACTION.equals(action)) {
            handleChangeStateIntent(context, intent);
        } else if (SHOW_AND_DISMISS_ALARM_ACTION.equals(action)) {
            Uri uri = intent.getData();
            AlarmInstance instance = AlarmInstance.getInstance(context.getContentResolver(),
                    AlarmInstance.getId(uri));
            if (instance == null) {
                // Not a big deal, but it shouldn't happen
                LogUtils.e("Can not show and dismiss alarm for unknown instance: " + uri);
                return;
            }
            long alarmId = instance.mAlarmId == null ? Alarm.INVALID_ID : instance.mAlarmId;
            Intent viewAlarmIntent = Alarm.createIntent(context, DeskClock.class, alarmId);
            viewAlarmIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.ALARM_TAB_INDEX);
            viewAlarmIntent.putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, alarmId);
            viewAlarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(viewAlarmIntent);
            setDismissState(context, instance);
        }
    }

    private static void handleChangeStateIntent(Context context, Intent intent) {
        Uri uri = intent.getData();
        AlarmInstance instance = AlarmInstance.getInstance(context.getContentResolver(),
                AlarmInstance.getId(uri));
@@ -749,91 +784,59 @@ public final class AlarmStateManager extends BroadcastReceiver {
        int intentId = intent.getIntExtra(ALARM_GLOBAL_ID_EXTRA, -1);
        int alarmState = intent.getIntExtra(ALARM_STATE_EXTRA, -1);
        if (intentId != globalId) {
                LogUtils.i("IntentId: " + intentId + " GlobalId: " + globalId + " AlarmState: " +
                        alarmState);
                // Allows dismiss/snooze requests to go through
                if (!intent.hasCategory(ALARM_DISMISS_TAG) &&
                        !intent.hasCategory(ALARM_SNOOZE_TAG)) {
                    LogUtils.i("Ignoring old Intent");
            LogUtils.i("Ignoring old Intent. IntentId: " + intentId + " GlobalId: " + globalId +
                    " AlarmState: " + alarmState);
            return;
        }
            }

            // If the phone is busy, keep the alarm snoozing.When the call is ended,
            // the new coming alarm or the alarm which wakes from sooze,will skip the codes here
            // and continue show the alarm as normal.
        // If the phone is busy, add the alarm to a string set in shared preferenecs that will be
        // cleared when the call is ended.
        if (context.getResources().getBoolean(R.bool.config_delayalarm)) {
            TelephonyManager mTelephonyManager = (TelephonyManager) context
                    .getSystemService(Context.TELEPHONY_SERVICE);
            if ((mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE)
                    && (alarmState == AlarmInstance.FIRED_STATE)) {
                    snooze(context, intent, instance);
                pendAlarm(context, uri, alarmState);
                return;
            }
        }

        setChangeAlarmState(context, instance, alarmState);
    }

    public static void setChangeAlarmState(Context context, AlarmInstance instance,
        int alarmState) {

        if (alarmState >= 0) {
            setAlarmState(context, instance, alarmState);
        } else {
            // No need to register instance again when alarmState
            // equals POWER_OFF_ALARM_STATE. POWER_OFF_ALARM_STATE
            // is an invalid state for rtc power off alarm.
                if (alarmState == AlarmInstance.POWER_OFF_ALARM_STATE)
                {
            if (alarmState == AlarmInstance.POWER_OFF_ALARM_STATE) {
                return;
            }
            registerInstance(context, instance, true);
        }
        } else if (SHOW_AND_DISMISS_ALARM_ACTION.equals(action)) {
            Uri uri = intent.getData();
            AlarmInstance instance = AlarmInstance.getInstance(context.getContentResolver(),
                    AlarmInstance.getId(uri));
            if (instance == null) {
                // Not a big deal, but it shouldn't happen
                LogUtils.e("Can not show and dismiss alarm for unknown instance: " + uri);
                return;
            }
            long alarmId = instance.mAlarmId == null ? Alarm.INVALID_ID : instance.mAlarmId;
            Intent viewAlarmIntent = Alarm.createIntent(context, DeskClock.class, alarmId);
            viewAlarmIntent.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.ALARM_TAB_INDEX);
            viewAlarmIntent.putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, alarmId);
            viewAlarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(viewAlarmIntent);
            setDismissState(context, instance);
        }
    }

    /**
     * Make the alarm snooze based on the snooze interval in settings.
     * If the phone is not busy in call anymore, this method will not be
     * called, and the alarm will wake up based on snooze interval.
     * Add the alarm to list of pending Alarms to be fired after the call is complete.
     */
    private void snooze(Context context, Intent intent, AlarmInstance instance) {
        Uri uri = intent.getData();
        AlarmInstance newInstance = AlarmInstance.getInstance(context.getContentResolver(),
                AlarmInstance.getId(uri));
        if (newInstance == null) {
            // If AlarmInstance is turn to null,return.
            return;
        }
    private static void pendAlarm(Context context, Uri uri, int alarmState) {
        LogUtils.v("Pending alarm: " + uri);

        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        Set<String> alarms = sp.getStringSet(ALARM_PENDING_ALARM_KEY, new HashSet<String>());

        // Alarms are stored as "<uri>|<alarmState>"
        String alarm = uri.toString() + "|" + alarmState;

        HashSet<String> newSet = new HashSet<String>();
        newSet.addAll(alarms);
        newSet.add(alarm);

        // Notify the user that the alarm has been snoozed.
        Intent cancelSnooze = createStateChangeIntent(context, ALARM_MANAGER_TAG, newInstance,
                AlarmInstance.DISMISSED_STATE);
        PendingIntent broadcast = PendingIntent.getBroadcast(context, instance.hashCode(),
                cancelSnooze, 0);
        String label = newInstance.getLabelOrDefault(context);
        label = context.getString(R.string.alarm_notify_snooze_label, label);
        NotificationManager nm = (NotificationManager) context
                .getSystemService(Context.NOTIFICATION_SERVICE);
        Notification n = new Notification(R.drawable.stat_notify_alarm, label, 0);
        n.setLatestEventInfo(context, label,
                context.getString(R.string.alarm_notify_snooze_text,
                        AlarmUtils.getFormattedTime(context, instance.getAlarmTime())),
                broadcast);
        n.flags |= Notification.FLAG_AUTO_CANCEL | Notification.FLAG_ONGOING_EVENT;
        nm.notify(instance.hashCode(), n);
        setAlarmState(context, instance, AlarmInstance.SNOOZE_STATE);
        sp.edit().putStringSet(ALARM_PENDING_ALARM_KEY, newSet).commit();
    }

    /**
+69 −0
Original line number Diff line number Diff line
package com.android.deskclock.alarms;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.android.deskclock.LogUtils;
import com.android.deskclock.provider.AlarmInstance;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * Watch the phone state to clear any alarms that are waiting for a call to end
 */
public class PhoneStateReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        LogUtils.v("PhoneStateReceiver received intent " + intent);

        if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
            TelephonyManager mTelephonyManager = (TelephonyManager) context
                    .getSystemService(Context.TELEPHONY_SERVICE);
            if (mTelephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
                // New call state is idle, update state for any pending alarms
                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
                Set<String> alarms = sp.getStringSet(AlarmStateManager.ALARM_PENDING_ALARM_KEY,
                        new HashSet<String>());
                if (alarms.size() <= 0) {
                    return; // no alarms to fire
                }

                Iterator<String> iterator = alarms.iterator();

                while (iterator.hasNext()) {
                    String flatAlarm = iterator.next();
                    if (TextUtils.isEmpty(flatAlarm)) {
                        LogUtils.e("Unable to un-flatten alarm for restore");
                        return;
                    }

                    String [] items = flatAlarm.split("\\|");
                    if (items.length < 2) {
                        LogUtils.e("Unable to un-flatten alarm for restore");
                        return;
                    }

                    Uri uri = Uri.parse(items[0]);
                    AlarmInstance instance = AlarmInstance.getInstance(
                            context.getContentResolver(), AlarmInstance.getId(uri));
                    int alarmState = Integer.parseInt(items[1]);

                    AlarmStateManager.setChangeAlarmState(context, instance, alarmState);
                    iterator.remove();
                }

                // Clear out the pending alarms
                sp.edit().remove(AlarmStateManager.ALARM_PENDING_ALARM_KEY).commit();
            }
        }
    }
}
 No newline at end of file