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

Commit 7c5ac8fd authored by Julia Reynolds's avatar Julia Reynolds
Browse files

Support new conversation DND options

Test: atest
Bug: 137397357
Change-Id: I52420ffdc4b6618e66354a9c1bb99b0296bc4ea4
parent c240b278
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -1175,6 +1175,19 @@
        <item>zen_mode_from_none</item>
    </string-array>

    <string-array name="zen_mode_conversations_entries" translatable="false">
        <item>@string/zen_mode_from_all_conversations</item>
        <item>@string/zen_mode_from_important_conversations</item>
        <item>@string/zen_mode_from_no_conversations</item>
    </string-array>

    <!-- these values correspond with ZenPolicy.ConversationSenders -->
    <string-array name="zen_mode_conversations_values" translatable="false">
        <item>1</item>
        <item>2</item>
        <item>3</item>
    </string-array>

    <!--String arrays for notification swipe direction -->
    <string-array name="swipe_direction_titles">
        <item>@string/swipe_direction_rtl</item>
+3 −0
Original line number Diff line number Diff line
@@ -8637,6 +8637,9 @@
    </plurals>
    <string name="zen_mode_conversations_title">Conversations</string>
    <string name="zen_mode_from_all_conversations">From all conversations</string>
    <string name="zen_mode_from_important_conversations">From important conversations</string>
    <string name="zen_mode_from_no_conversations">Don\u2019t allow any conversations</string>
    <!-- [CHAR LIMIT=40] Zen mode settings: Messages option -->
    <string name="zen_mode_messages">Allow messages</string>
+7 −0
Original line number Diff line number Diff line
@@ -59,6 +59,13 @@
   <PreferenceCategory
       android:title="@string/zen_mode_conversations_title"
       android:key="zen_mode_settings_category_conversations">

      <!-- Conversations -->
      <ListPreference
          android:key="zen_mode_conversations"
          android:title="@string/zen_mode_conversations_title"
          android:entries="@array/zen_mode_conversations_entries"
          android:entryValues="@array/zen_mode_conversations_values"/>
   </PreferenceCategory>

   <!-- Footer that shows if user is put into alarms only or total silence mode by an app -->
+0 −210
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.settings.notification.zen;

import android.app.Application;
import android.app.NotificationChannel;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;

import androidx.annotation.VisibleForTesting;
import androidx.core.text.BidiFormatter;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.notification.app.ChannelNotificationSettings;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.widget.apppreference.AppPreference;

import java.util.ArrayList;
import java.util.List;

/**
 * Adds a preference to the PreferenceScreen for each conversation notification channel that can
 * bypass DND.
 */
public class ZenModeAllBypassingConversationsPreferenceController extends
        AbstractPreferenceController implements PreferenceControllerMixin {

    private final String KEY = "zen_mode_settings_category_conversations";

    @VisibleForTesting ApplicationsState mApplicationsState;
    @VisibleForTesting PreferenceCategory mCategory;
    @VisibleForTesting Context mPrefContext;

    private ApplicationsState.Session mAppSession;
    private NotificationBackend mNotificationBackend = new NotificationBackend();
    private Fragment mHostFragment;

    public ZenModeAllBypassingConversationsPreferenceController(Context context, Application app,
            Fragment host) {

        this(context, app == null ? null : ApplicationsState.getInstance(app), host);
    }

    private ZenModeAllBypassingConversationsPreferenceController(Context context,
            ApplicationsState appState, Fragment host) {
        super(context);
        mApplicationsState = appState;
        mHostFragment = host;

        if (mApplicationsState != null && host != null) {
            mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, host.getLifecycle());
        }
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        mCategory = screen.findPreference(KEY);
        mPrefContext = screen.getContext();
        updateNotificationChannelList();
        super.displayPreference(screen);
    }

    @Override
    public boolean isAvailable() {
        return true;
    }

    @Override
    public String getPreferenceKey() {
        return KEY;
    }

    /**
     * Call this method to trigger the notification channels list to refresh.
     */
    public void updateNotificationChannelList() {
        if (mAppSession == null) {
            return;
        }

        ApplicationsState.AppFilter filter = ApplicationsState.FILTER_ALL_ENABLED;
        List<ApplicationsState.AppEntry> apps = mAppSession.rebuild(filter,
                ApplicationsState.ALPHA_COMPARATOR);
        if (apps != null) {
            updateNotificationChannelList(apps);
        }
    }

    @VisibleForTesting
    void updateNotificationChannelList(List<ApplicationsState.AppEntry> apps) {
        if (mCategory == null || apps == null) {
            return;
        }

        List<Preference> channelsBypassingDnd = new ArrayList<>();
        for (ApplicationsState.AppEntry entry : apps) {
            String pkg = entry.info.packageName;
            mApplicationsState.ensureIcon(entry);
            for (NotificationChannel channel : mNotificationBackend
                    .getNotificationChannelsBypassingDnd(pkg, entry.info.uid).getList()) {
                if (TextUtils.isEmpty(channel.getConversationId())) {
                    // only conversation channels
                    continue;
                }
                Preference pref = new AppPreference(mPrefContext);
                pref.setKey(pkg + "|" + channel.getId());
                pref.setTitle(BidiFormatter.getInstance().unicodeWrap(entry.label));
                // TODO: use badged shortcut icon instead of app icon
                pref.setIcon(entry.icon);
                pref.setSummary(BidiFormatter.getInstance().unicodeWrap(channel.getName()));

                pref.setOnPreferenceClickListener(preference -> {
                    Bundle args = new Bundle();
                    args.putString(AppInfoBase.ARG_PACKAGE_NAME, entry.info.packageName);
                    args.putInt(AppInfoBase.ARG_PACKAGE_UID, entry.info.uid);
                    args.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
                    new SubSettingLauncher(mContext)
                            .setDestination(ChannelNotificationSettings.class.getName())
                            .setArguments(args)
                            .setTitleRes(R.string.notification_channel_title)
                            .setResultListener(mHostFragment, 0)
                            .setSourceMetricsCategory(
                                    SettingsEnums.NOTIFICATION_ZEN_MODE_OVERRIDING_APP)
                            .launch();
                    return true;
                });
                channelsBypassingDnd.add(pref);
            }

            mCategory.removeAll();
            if (channelsBypassingDnd.size() > 0) {
                mCategory.setVisible(true);
                for (Preference prefToAdd : channelsBypassingDnd) {
                    mCategory.addPreference(prefToAdd);
                }
            } else {
                mCategory.setVisible(false);
            }
        }
    }

    private final ApplicationsState.Callbacks mAppSessionCallbacks =
            new ApplicationsState.Callbacks() {

                @Override
                public void onRunningStateChanged(boolean running) {
                    updateNotificationChannelList();
                }

                @Override
                public void onPackageListChanged() {
                    updateNotificationChannelList();
                }

                @Override
                public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
                    updateNotificationChannelList(apps);
                }

                @Override
                public void onPackageIconChanged() {
                    updateNotificationChannelList();
                }

                @Override
                public void onPackageSizeChanged(String packageName) {
                    updateNotificationChannelList();
                }

                @Override
                public void onAllSizesComputed() { }

                @Override
                public void onLauncherInfoChanged() {
                    updateNotificationChannelList();
                }

                @Override
                public void onLoadEntriesCompleted() {
                    // Add shortcut info
                    updateNotificationChannelList();
                }
            };
}
+58 −16
Original line number Diff line number Diff line
@@ -16,8 +16,12 @@

package com.android.settings.notification.zen;

import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;

import android.app.ActivityManager;
import android.app.AutomaticZenRule;
@@ -147,25 +151,35 @@ public class ZenModeBackend {
        return SOURCE_NONE;
    }

    protected int getPriorityConversationSenders() {
        if (isPriorityCategoryEnabled(PRIORITY_CATEGORY_CONVERSATIONS)) {
            return mPolicy.priorityConversationSenders;
        }
        return CONVERSATION_SENDERS_NONE;
    }

    protected void saveVisualEffectsPolicy(int category, boolean suppress) {
        Settings.Secure.putInt(mContext.getContentResolver(),
                Settings.Secure.ZEN_SETTINGS_UPDATED, 1);

        int suppressedEffects = getNewSuppressedEffects(suppress, category);
        savePolicy(mPolicy.priorityCategories, mPolicy.priorityCallSenders,
                mPolicy.priorityMessageSenders, suppressedEffects);
                mPolicy.priorityMessageSenders, suppressedEffects,
                mPolicy.priorityConversationSenders);
    }

    protected void saveSoundPolicy(int category, boolean allow) {
        int priorityCategories = getNewDefaultPriorityCategories(allow, category);
        savePolicy(priorityCategories, mPolicy.priorityCallSenders,
                mPolicy.priorityMessageSenders, mPolicy.suppressedVisualEffects);
                mPolicy.priorityMessageSenders, mPolicy.suppressedVisualEffects,
                mPolicy.priorityConversationSenders);
    }

    protected void savePolicy(int priorityCategories, int priorityCallSenders,
            int priorityMessageSenders, int suppressedVisualEffects) {
            int priorityMessageSenders, int suppressedVisualEffects,
            int priorityConversationSenders) {
        mPolicy = new NotificationManager.Policy(priorityCategories, priorityCallSenders,
                priorityMessageSenders, suppressedVisualEffects);
                priorityMessageSenders, suppressedVisualEffects, priorityConversationSenders);
        mNotificationManager.setNotificationPolicy(mPolicy);
    }

@@ -210,23 +224,21 @@ public class ZenModeBackend {
        }

        savePolicy(getNewDefaultPriorityCategories(allowSenders, category),
            priorityCallSenders, priorityMessagesSenders, mPolicy.suppressedVisualEffects);
            priorityCallSenders, priorityMessagesSenders, mPolicy.suppressedVisualEffects,
                mPolicy.priorityConversationSenders);

        if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allow" +
                stringCategory + "=" + allowSenders + " allow" + stringCategory + "From="
                + ZenModeConfig.sourceToString(allowSendersFrom));
    }

    protected String getSendersKey(int category) {
        switch (getZenMode()) {
            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
            case Settings.Global.ZEN_MODE_ALARMS:
                return getKeyFromSetting(SOURCE_NONE);
            default:
                int prioritySenders = getPrioritySenders(category);
                return getKeyFromSetting(isPriorityCategoryEnabled(category)
                        ? prioritySenders : SOURCE_NONE);
            }
    protected void saveConversationSenders(int val) {
        final boolean allowSenders = val != CONVERSATION_SENDERS_NONE;

        savePolicy(getNewDefaultPriorityCategories(allowSenders, PRIORITY_CATEGORY_CONVERSATIONS),
                mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
                mPolicy.suppressedVisualEffects, val);

    }

    private int getPrioritySenders(int category) {
@@ -240,6 +252,10 @@ public class ZenModeBackend {
            return getPriorityMessageSenders();
        }

        if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS) {
            return getPriorityConversationSenders();
        }

        return categorySenders;
    }

@@ -271,11 +287,13 @@ public class ZenModeBackend {
        }
    }

    protected int getAlarmsTotalSilenceCallsMessagesSummary(int category) {
    protected int getAlarmsTotalSilencePeopleSummary(int category) {
        if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
            return R.string.zen_mode_from_none_messages;
        } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS){
            return R.string.zen_mode_from_none_calls;
        } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS) {
            return R.string.zen_mode_from_no_conversations;
        }
        return R.string.zen_mode_from_none;
    }
@@ -309,6 +327,21 @@ public class ZenModeBackend {
        }
    }

    protected int getConversationSummary() {
        int conversationType = getPriorityConversationSenders();

        switch (conversationType) {
            case NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE:
                return R.string.zen_mode_from_all_conversations;
            case NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT:
                return R.string.zen_mode_from_important_conversations;
            case NotificationManager.Policy.CONVERSATION_SENDERS_NONE:
                return R.string.zen_mode_from_no_conversations;
            default:
                return R.string.zen_mode_from_no_conversations;
        }
    }

    protected int getContactsCallsSummary(ZenPolicy policy) {
        int peopleType = policy.getPriorityCallSenders();
        switch (peopleType) {
@@ -398,12 +431,21 @@ public class ZenModeBackend {
            messages = ZenPolicy.PEOPLE_TYPE_NONE;
        }

        int conversations;
        if (mPolicy.allowConversations()) {
            // unlike the above, no mapping is needed because the values are the same
            conversations = mPolicy.allowConversationsFrom();
        } else {
            conversations = CONVERSATION_SENDERS_NONE;
        }

        return new ZenPolicy.Builder(zenPolicy)
                .allowAlarms(mPolicy.allowAlarms())
                .allowCalls(calls)
                .allowEvents(mPolicy.allowEvents())
                .allowMedia(mPolicy.allowMedia())
                .allowMessages(messages)
                .allowConversations(conversations)
                .allowReminders(mPolicy.allowReminders())
                .allowRepeatCallers(mPolicy.allowRepeatCallers())
                .allowSystem(mPolicy.allowSystem())
Loading