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

Commit 1cba7840 authored by Julia Reynolds's avatar Julia Reynolds Committed by Android (Google) Code Review
Browse files

Merge "Support new conversation DND options"

parents a595c272 7c5ac8fd
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
@@ -8648,6 +8648,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