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

Commit 1d7d2248 authored by John Spurlock's avatar John Spurlock
Browse files

Zen: New option to allow repeat callers.

Bug: 20064962
Change-Id: I11a5519c02bf8fa8e332559092c865c5e612fbd2
parent 4c42bc04
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -68,12 +68,14 @@ public class ZenModeConfig implements Parcelable {

    private static final boolean DEFAULT_ALLOW_REMINDERS = true;
    private static final boolean DEFAULT_ALLOW_EVENTS = true;
    private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;

    private static final int XML_VERSION = 2;
    private static final String ZEN_TAG = "zen";
    private static final String ZEN_ATT_VERSION = "version";
    private static final String ALLOW_TAG = "allow";
    private static final String ALLOW_ATT_CALLS = "calls";
    private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers";
    private static final String ALLOW_ATT_MESSAGES = "messages";
    private static final String ALLOW_ATT_FROM = "from";
    private static final String ALLOW_ATT_REMINDERS = "reminders";
@@ -101,6 +103,7 @@ public class ZenModeConfig implements Parcelable {
    private static final String RULE_ATT_CONDITION_ID = "conditionId";

    public boolean allowCalls;
    public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
    public boolean allowMessages;
    public boolean allowReminders = DEFAULT_ALLOW_REMINDERS;
    public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
@@ -113,6 +116,7 @@ public class ZenModeConfig implements Parcelable {

    public ZenModeConfig(Parcel source) {
        allowCalls = source.readInt() == 1;
        allowRepeatCallers = source.readInt() == 1;
        allowMessages = source.readInt() == 1;
        allowReminders = source.readInt() == 1;
        allowEvents = source.readInt() == 1;
@@ -133,6 +137,7 @@ public class ZenModeConfig implements Parcelable {
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(allowCalls ? 1 : 0);
        dest.writeInt(allowRepeatCallers ? 1 : 0);
        dest.writeInt(allowMessages ? 1 : 0);
        dest.writeInt(allowReminders ? 1 : 0);
        dest.writeInt(allowEvents ? 1 : 0);
@@ -158,6 +163,7 @@ public class ZenModeConfig implements Parcelable {
    public String toString() {
        return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
            .append("allowCalls=").append(allowCalls)
            .append(",allowRepeatCallers=").append(allowRepeatCallers)
            .append(",allowMessages=").append(allowMessages)
            .append(",allowFrom=").append(sourceToString(allowFrom))
            .append(",allowReminders=").append(allowReminders)
@@ -213,6 +219,7 @@ public class ZenModeConfig implements Parcelable {
        if (o == this) return true;
        final ZenModeConfig other = (ZenModeConfig) o;
        return other.allowCalls == allowCalls
                && other.allowRepeatCallers == allowRepeatCallers
                && other.allowMessages == allowMessages
                && other.allowFrom == allowFrom
                && other.allowReminders == allowReminders
@@ -223,8 +230,8 @@ public class ZenModeConfig implements Parcelable {

    @Override
    public int hashCode() {
        return Objects.hash(allowCalls, allowMessages, allowFrom, allowReminders, allowEvents,
                automaticRules, manualRule);
        return Objects.hash(allowCalls, allowRepeatCallers, allowMessages, allowFrom,
                allowReminders, allowEvents, automaticRules, manualRule);
    }

    private static String toDayList(int[] days) {
@@ -279,6 +286,8 @@ public class ZenModeConfig implements Parcelable {
            if (type == XmlPullParser.START_TAG) {
                if (ALLOW_TAG.equals(tag)) {
                    rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
                    rt.allowRepeatCallers = safeBoolean(parser, ALLOW_ATT_REPEAT_CALLERS,
                            DEFAULT_ALLOW_REPEAT_CALLERS);
                    rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false);
                    rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
                            DEFAULT_ALLOW_REMINDERS);
@@ -307,6 +316,7 @@ public class ZenModeConfig implements Parcelable {

        out.startTag(null, ALLOW_TAG);
        out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
        out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers));
        out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
        out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders));
        out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents));
+3 −0
Original line number Diff line number Diff line
@@ -2047,6 +2047,9 @@
        <item>schedule</item>
    </string-array>

    <!-- Priority repeat caller threshold, in minutes -->
    <integer name="config_zen_repeat_callers_threshold">15</integer>

    <!-- Flags enabling default window features. See Window.java -->
    <bool name="config_defaultWindowFeatureOptionsPanel">true</bool>
    <bool name="config_defaultWindowFeatureContextMenu">true</bool>
+1 −0
Original line number Diff line number Diff line
@@ -2230,4 +2230,5 @@
  <java-symbol type="string" name="date_picker_next_month_button" />
  <java-symbol type="layout" name="date_picker_month_item_material" />
  <java-symbol type="id" name="month_view" />
  <java-symbol type="integer" name="config_zen_repeat_callers_threshold" />
</resources>
+83 −6
Original line number Diff line number Diff line
@@ -27,14 +27,19 @@ import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.service.notification.ZenModeConfig;
import android.telecom.TelecomManager;
import android.util.ArrayMap;
import android.util.Slog;

import java.io.PrintWriter;
import java.util.Date;
import java.util.Objects;

public class ZenModeFiltering {
    private static final String TAG = ZenModeHelper.TAG;
    private static final boolean DEBUG = ZenModeHelper.DEBUG;

    static final RepeatCallers REPEAT_CALLERS = new RepeatCallers();

    private final Context mContext;

    private ComponentName mDefaultPhoneApp;
@@ -43,8 +48,25 @@ public class ZenModeFiltering {
        mContext = context;
    }

    public ComponentName getDefaultPhoneApp() {
        return mDefaultPhoneApp;
    public void dump(PrintWriter pw, String prefix) {
        pw.print(prefix); pw.print("mDefaultPhoneApp="); pw.println(mDefaultPhoneApp);
        pw.print(prefix); pw.print("RepeatCallers.mThresholdMinutes=");
        pw.println(REPEAT_CALLERS.mThresholdMinutes);
        synchronized (REPEAT_CALLERS) {
            if (!REPEAT_CALLERS.mCalls.isEmpty()) {
                pw.print(prefix); pw.println("RepeatCallers.mCalls=");
                for (int i = 0; i < REPEAT_CALLERS.mCalls.size(); i++) {
                    pw.print(prefix); pw.print("  ");
                    pw.print(REPEAT_CALLERS.mCalls.keyAt(i));
                    pw.print(" at ");
                    pw.println(ts(REPEAT_CALLERS.mCalls.valueAt(i)));
                }
            }
        }
    }

    private static String ts(long time) {
        return new Date(time) + " (" + time + ")";
    }

    /**
@@ -53,13 +75,14 @@ public class ZenModeFiltering {
     * @param timeoutAffinity affinity to return when the timeout specified via
     *                        <code>contactsTimeoutMs</code> is hit
     */
    public static boolean matchesCallFilter(int zen, ZenModeConfig config, UserHandle userHandle,
            Bundle extras, ValidateNotificationPeople validator, int contactsTimeoutMs,
            float timeoutAffinity) {
    public static boolean matchesCallFilter(Context context, int zen, ZenModeConfig config,
            UserHandle userHandle, Bundle extras, ValidateNotificationPeople validator,
            int contactsTimeoutMs, float timeoutAffinity) {
        if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
        if (zen == Global.ZEN_MODE_ALARMS) return false; // not an alarm
        if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
            if (!config.allowCalls) return false; // no calls get through
            if (config.allowRepeatCallers && REPEAT_CALLERS.isRepeat(context, extras)) return true;
            if (!config.allowCalls) return false; // no other calls get through
            if (validator != null) {
                final float contactAffinity = validator.getContactAffinity(userHandle, extras,
                        contactsTimeoutMs, timeoutAffinity);
@@ -69,6 +92,11 @@ public class ZenModeFiltering {
        return true;
    }

    private static Bundle extras(NotificationRecord record) {
        return record != null && record.sbn != null && record.sbn.getNotification() != null
                ? record.sbn.getNotification().extras : null;
    }

    public boolean shouldIntercept(int zen, ZenModeConfig config, NotificationRecord record) {
        if (isSystem(record)) {
            return false;
@@ -96,6 +124,11 @@ public class ZenModeFiltering {
                    return false;
                }
                if (isCall(record)) {
                    if (config.allowRepeatCallers
                            && REPEAT_CALLERS.isRepeat(mContext, extras(record))) {
                        ZenLog.traceNotIntercepted(record, "repeatCaller");
                        return false;
                    }
                    if (!config.allowCalls) {
                        ZenLog.traceIntercepted(record, "!allowCalls");
                        return true;
@@ -199,4 +232,48 @@ public class ZenModeFiltering {
                return true;
        }
    }

    private static class RepeatCallers {
        private final ArrayMap<String, Long> mCalls = new ArrayMap<>();
        private int mThresholdMinutes;

        private synchronized boolean isRepeat(Context context, Bundle extras) {
            if (mThresholdMinutes <= 0) {
                mThresholdMinutes = context.getResources().getInteger(com.android.internal.R.integer
                        .config_zen_repeat_callers_threshold);
            }
            if (mThresholdMinutes <= 0 || extras == null) return false;
            final String peopleString = peopleString(extras);
            if (peopleString == null) return false;
            final long now = System.currentTimeMillis();
            final int N = mCalls.size();
            for (int i = N - 1; i >= 0; i--) {
                final long time = mCalls.valueAt(i);
                if (time > now || (now - time) > mThresholdMinutes * 1000 * 60) {
                    mCalls.removeAt(i);
                }
            }
            final boolean isRepeat = mCalls.containsKey(peopleString);
            mCalls.put(peopleString, now);
            return isRepeat;
        }

        private static String peopleString(Bundle extras) {
            final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras);
            if (extraPeople == null || extraPeople.length == 0) return null;
            final StringBuilder sb = new StringBuilder();
            for (int i = 0; i < extraPeople.length; i++) {
                String extraPerson = extraPeople[i];
                if (extraPerson == null) continue;
                extraPerson = extraPerson.trim();
                if (extraPerson.isEmpty()) continue;
                if (sb.length() > 0) {
                    sb.append('|');
                }
                sb.append(extraPerson);
            }
            return sb.length() == 0 ? null : sb.toString();
        }
    }

}
+8 −7
Original line number Diff line number Diff line
@@ -101,8 +101,8 @@ public class ZenModeHelper {

    public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
            ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) {
        return ZenModeFiltering.matchesCallFilter(mZenMode, mConfig, userHandle, extras, validator,
                contactsTimeoutMs, timeoutAffinity);
        return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConfig, userHandle, extras,
                validator, contactsTimeoutMs, timeoutAffinity);
    }

    public boolean isCall(NotificationRecord record) {
@@ -195,8 +195,8 @@ public class ZenModeHelper {
        dump(pw, prefix, "mConfig", mConfig);
        dump(pw, prefix, "mDefaultConfig", mDefaultConfig);
        pw.print(prefix); pw.print("mPreviousRingerMode="); pw.println(mPreviousRingerMode);
        pw.print(prefix); pw.print("DefaultPhoneApp="); pw.println(mFiltering.getDefaultPhoneApp());
        pw.print(prefix); pw.print("mEffectsSuppressed="); pw.println(mEffectsSuppressed);
        mFiltering.dump(pw, prefix);
        mConditions.dump(pw, prefix);
    }

@@ -206,9 +206,9 @@ public class ZenModeHelper {
            pw.println(config);
            return;
        }
        pw.printf("allow(calls=%s,events=%s,from=%s,messages=%s,reminders=%s)\n",
                config.allowCalls, config.allowEvents, config.allowFrom, config.allowMessages,
                config.allowReminders);
        pw.printf("allow(calls=%s,repeatCallers=%s,events=%s,from=%s,messages=%s,reminders=%s)\n",
                config.allowCalls, config.allowRepeatCallers, config.allowEvents, config.allowFrom,
                config.allowMessages, config.allowReminders);
        pw.print(prefix); pw.print("  manualRule="); pw.println(config.manualRule);
        if (config.automaticRules.isEmpty()) return;
        final int N = config.automaticRules.size();
@@ -304,7 +304,8 @@ public class ZenModeHelper {
        applyRestrictions(muteNotifications, USAGE_NOTIFICATION);

        // call restrictions
        final boolean muteCalls = zen && !mConfig.allowCalls || mEffectsSuppressed;
        final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers
                || mEffectsSuppressed;
        applyRestrictions(muteCalls, USAGE_NOTIFICATION_RINGTONE);

        // alarm restrictions