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

Commit 03ebcc24 authored by Jeff DeCew's avatar Jeff DeCew Committed by Automerger Merge Worker
Browse files

Merge "Update NotificationChannelSettings Preferences in place to prevent...

Merge "Update NotificationChannelSettings Preferences in place to prevent re-layout." into rvc-qpr-dev am: e9523b76

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/12792910

Change-Id: I55f6310b344baebfe6a4425d3644d22d8e479638
parents 2f3b18d9 e9523b76
Loading
Loading
Loading
Loading
+188 −81
Original line number Diff line number Diff line
@@ -23,16 +23,14 @@ import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.graphics.BlendMode;
import android.graphics.BlendModeColorFilter;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
@@ -53,7 +51,8 @@ import java.util.List;
public class ChannelListPreferenceController extends NotificationPreferenceController {

    private static final String KEY = "channels";
    private static String KEY_GENERAL_CATEGORY = "categories";
    private static final String KEY_GENERAL_CATEGORY = "categories";
    private static final String KEY_ZERO_CATEGORIES = "zeroCategories";
    public static final String ARG_FROM_SETTINGS = "fromSettings";

    private List<NotificationChannelGroup> mChannelGroupList;
@@ -102,62 +101,192 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr
                if (mContext == null) {
                    return;
                }
                populateList();
                updateFullList(mPreference, mChannelGroupList);
            }
        }.execute();
    }

    private void populateList() {
        // TODO: if preference has children, compare with newly loaded list
        mPreference.removeAll();
    /**
     * Update the preferences group to match the
     * @param groupPrefsList
     * @param channelGroups
     */
    void updateFullList(@NonNull PreferenceCategory groupPrefsList,
                @NonNull List<NotificationChannelGroup> channelGroups) {
        if (channelGroups.isEmpty()) {
            if (groupPrefsList.getPreferenceCount() == 1
                    && KEY_ZERO_CATEGORIES.equals(groupPrefsList.getPreference(0).getKey())) {
                // Ensure the titles are correct for the current language, but otherwise leave alone
                PreferenceGroup groupCategory = (PreferenceGroup) groupPrefsList.getPreference(0);
                groupCategory.setTitle(R.string.notification_channels);
                groupCategory.getPreference(0).setTitle(R.string.no_channels);
            } else {
                // Clear any contents and create the 'zero-categories' group.
                groupPrefsList.removeAll();

        if (mChannelGroupList.isEmpty()) {
                PreferenceCategory groupCategory = new PreferenceCategory(mContext);
                groupCategory.setTitle(R.string.notification_channels);
            groupCategory.setKey(KEY_GENERAL_CATEGORY);
            mPreference.addPreference(groupCategory);
                groupCategory.setKey(KEY_ZERO_CATEGORIES);
                groupPrefsList.addPreference(groupCategory);

                Preference empty = new Preference(mContext);
                empty.setTitle(R.string.no_channels);
                empty.setEnabled(false);
                groupCategory.addPreference(empty);
            }
        } else {
            populateGroupList();
            updateGroupList(groupPrefsList, channelGroups);
        }
    }

    private void populateGroupList() {
        for (NotificationChannelGroup group : mChannelGroupList) {
    /**
     * Looks for the category for the given group's key at the expected index, if that doesn't
     * match, it checks all groups, and if it can't find that group anywhere, it creates it.
     */
    @NonNull
    private PreferenceCategory findOrCreateGroupCategoryForKey(
            @NonNull PreferenceCategory groupPrefsList, @Nullable String key, int expectedIndex) {
        if (key == null) {
            key = KEY_GENERAL_CATEGORY;
        }
        int preferenceCount = groupPrefsList.getPreferenceCount();
        if (expectedIndex < preferenceCount) {
            Preference preference = groupPrefsList.getPreference(expectedIndex);
            if (key.equals(preference.getKey())) {
                return (PreferenceCategory) preference;
            }
        }
        for (int i = 0; i < preferenceCount; i++) {
            Preference preference = groupPrefsList.getPreference(i);
            if (key.equals(preference.getKey())) {
                preference.setOrder(expectedIndex);
                return (PreferenceCategory) preference;
            }
        }
        PreferenceCategory groupCategory = new PreferenceCategory(mContext);
            groupCategory.setOrderingAsAdded(true);
            mPreference.addPreference(groupCategory);
        groupCategory.setOrder(expectedIndex);
        groupCategory.setKey(key);
        groupPrefsList.addPreference(groupCategory);
        return groupCategory;
    }

    private void updateGroupList(@NonNull PreferenceCategory groupPrefsList,
            @NonNull List<NotificationChannelGroup> channelGroups) {
        // Update the list, but optimize for the most common case where the list hasn't changed.
        int numFinalGroups = channelGroups.size();
        int initialPrefCount = groupPrefsList.getPreferenceCount();
        List<PreferenceCategory> finalOrderedGroups = new ArrayList<>(numFinalGroups);
        for (int i = 0; i < numFinalGroups; i++) {
            NotificationChannelGroup group = channelGroups.get(i);
            PreferenceCategory groupCategory =
                    findOrCreateGroupCategoryForKey(groupPrefsList, group.getId(), i);
            finalOrderedGroups.add(groupCategory);
            updateGroupPreferences(group, groupCategory);
        }
        int postAddPrefCount = groupPrefsList.getPreferenceCount();
        // If any groups were inserted (into a non-empty list) or need to be removed, we need to
        // remove all groups and re-add them all.
        // This is required to ensure proper ordering of inserted groups, and it simplifies logic
        // at the cost of computation in the rare case that the list is changing.
        boolean hasInsertions = initialPrefCount != 0 && initialPrefCount != numFinalGroups;
        boolean requiresRemoval = postAddPrefCount != numFinalGroups;
        if (hasInsertions || requiresRemoval) {
            groupPrefsList.removeAll();
            for (PreferenceCategory group : finalOrderedGroups) {
                groupPrefsList.addPreference(group);
            }
        }
    }

    /**
     * Looks for the channel preference for the given channel's key at the expected index, if that
     * doesn't match, it checks all rows, and if it can't find that channel anywhere, it creates
     * the preference.
     */
    @NonNull
    private MasterSwitchPreference findOrCreateChannelPrefForKey(
            @NonNull PreferenceGroup groupPrefGroup, @NonNull String key, int expectedIndex) {
        int preferenceCount = groupPrefGroup.getPreferenceCount();
        if (expectedIndex < preferenceCount) {
            Preference preference = groupPrefGroup.getPreference(expectedIndex);
            if (key.equals(preference.getKey())) {
                return (MasterSwitchPreference) preference;
            }
        }
        for (int i = 0; i < preferenceCount; i++) {
            Preference preference = groupPrefGroup.getPreference(i);
            if (key.equals(preference.getKey())) {
                preference.setOrder(expectedIndex);
                return (MasterSwitchPreference) preference;
            }
        }
        MasterSwitchPreference channelPref = new MasterSwitchPreference(mContext);
        channelPref.setOrder(expectedIndex);
        channelPref.setKey(key);
        groupPrefGroup.addPreference(channelPref);
        return channelPref;
    }

    private void updateGroupPreferences(@NonNull NotificationChannelGroup group,
            @NonNull PreferenceGroup groupPrefGroup) {
        int initialPrefCount = groupPrefGroup.getPreferenceCount();
        List<Preference> finalOrderedPrefs = new ArrayList<>();
        if (group.getId() == null) {
                groupCategory.setTitle(R.string.notification_channels_other);
                groupCategory.setKey(KEY_GENERAL_CATEGORY);
            // For the 'null' group, set the "Other" title.
            groupPrefGroup.setTitle(R.string.notification_channels_other);
        } else {
                groupCategory.setTitle(group.getName());
                groupCategory.setKey(group.getId());
                populateGroupToggle(groupCategory, group);
            // For an app-defined group, set their name and create a row to toggle 'isBlocked'.
            groupPrefGroup.setTitle(group.getName());
            finalOrderedPrefs.add(addOrUpdateGroupToggle(groupPrefGroup, group));
        }
            if (!group.isBlocked()) {
                final List<NotificationChannel> channels = group.getChannels();
        // Here "empty" means having no channel rows; the group toggle is ignored for this purpose.
        boolean initiallyEmpty = groupPrefGroup.getPreferenceCount() == finalOrderedPrefs.size();

        // For each channel, add or update the preference object.
        final List<NotificationChannel> channels =
                group.isBlocked() ? Collections.emptyList() : group.getChannels();
        Collections.sort(channels, CHANNEL_COMPARATOR);
                int N = channels.size();
                for (int i = 0; i < N; i++) {
                    final NotificationChannel channel = channels.get(i);
        for (NotificationChannel channel : channels) {
            if (!TextUtils.isEmpty(channel.getConversationId()) && !channel.isDemoted()) {
                // conversations get their own section
                    if (TextUtils.isEmpty(channel.getConversationId()) || channel.isDemoted()) {
                        populateSingleChannelPrefs(groupCategory, channel, group.isBlocked());
                continue;
            }
            // Get or create the row, and populate its current state.
            MasterSwitchPreference channelPref = findOrCreateChannelPrefForKey(groupPrefGroup,
                    channel.getId(), /* expectedIndex */ finalOrderedPrefs.size());
            updateSingleChannelPrefs(channelPref, channel, group.isBlocked());
            finalOrderedPrefs.add(channelPref);
        }
        int postAddPrefCount = groupPrefGroup.getPreferenceCount();

        // If any channels were inserted (into a non-empty list) or need to be removed, we need to
        // remove all preferences and re-add them all.
        // This is required to ensure proper ordering of inserted channels, and it simplifies logic
        // at the cost of computation in the rare case that the list is changing.
        int numFinalGroups = finalOrderedPrefs.size();
        boolean hasInsertions = !initiallyEmpty && initialPrefCount != numFinalGroups;
        boolean requiresRemoval = postAddPrefCount != numFinalGroups;
        if (hasInsertions || requiresRemoval) {
            groupPrefGroup.removeAll();
            for (Preference preference : finalOrderedPrefs) {
                groupPrefGroup.addPreference(preference);
            }
        }
    }

    protected void populateGroupToggle(final PreferenceGroup parent,
            NotificationChannelGroup group) {
        RestrictedSwitchPreference preference =
                new RestrictedSwitchPreference(mContext);
    /** Add or find and update the toggle for disabling the entire notification channel group. */
    private Preference addOrUpdateGroupToggle(@NonNull final PreferenceGroup parent,
            @NonNull final NotificationChannelGroup group) {
        boolean shouldAdd = false;
        final RestrictedSwitchPreference preference;
        if (parent.getPreferenceCount() > 0
                && parent.getPreference(0) instanceof RestrictedSwitchPreference) {
            preference = (RestrictedSwitchPreference) parent.getPreference(0);
        } else {
            shouldAdd = true;
            preference = new RestrictedSwitchPreference(mContext);
        }
        preference.setOrder(-1);
        preference.setTitle(mContext.getString(
                R.string.notification_switch_label, group.getName()));
        preference.setEnabled(mAdmin == null
@@ -171,23 +300,26 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr
            onGroupBlockStateChanged(group);
            return true;
        });

        if (shouldAdd) {
            parent.addPreference(preference);
        }
        return preference;
    }

    protected Preference populateSingleChannelPrefs(PreferenceGroup parent,
            final NotificationChannel channel, final boolean groupBlocked) {
        MasterSwitchPreference channelPref = new MasterSwitchPreference(mContext);
    /** Update the properties of the channel preference with the values from the channel object. */
    private void updateSingleChannelPrefs(@NonNull final MasterSwitchPreference channelPref,
            @NonNull final NotificationChannel channel,
            final boolean groupBlocked) {
        channelPref.setSwitchEnabled(mAdmin == null
                && isChannelBlockable(channel)
                && isChannelConfigurable(channel)
                && !groupBlocked);
        channelPref.setIcon(null);
        if (channel.getImportance() > IMPORTANCE_LOW) {
            channelPref.setIcon(getAlertingIcon());
        } else {
            channelPref.setIcon(null);
        }
        channelPref.setIconSize(MasterSwitchPreference.ICON_SIZE_SMALL);
        channelPref.setKey(channel.getId());
        channelPref.setTitle(channel.getName());
        channelPref.setSummary(NotificationBackend.getSentSummary(
                mContext, mAppRow.sentByChannel.get(channel.getId()), false));
@@ -219,10 +351,6 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr

                    return true;
                });
        if (parent.findPreference(channelPref.getKey()) == null) {
            parent.addPreference(channelPref);
        }
        return channelPref;
    }

    private Drawable getAlertingIcon() {
@@ -235,30 +363,9 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr
        if (group == null) {
            return;
        }
        PreferenceGroup groupGroup = mPreference.findPreference(group.getId());

        if (groupGroup != null) {
            if (group.isBlocked()) {
                List<Preference> toRemove = new ArrayList<>();
                int childCount = groupGroup.getPreferenceCount();
                for (int i = 0; i < childCount; i++) {
                    Preference pref = groupGroup.getPreference(i);
                    if (pref instanceof MasterSwitchPreference) {
                        toRemove.add(pref);
                    }
                }
                for (Preference pref : toRemove) {
                    groupGroup.removePreference(pref);
                }
            } else {
                final List<NotificationChannel> channels = group.getChannels();
                Collections.sort(channels, CHANNEL_COMPARATOR);
                int N = channels.size();
                for (int i = 0; i < N; i++) {
                    final NotificationChannel channel = channels.get(i);
                    populateSingleChannelPrefs(groupGroup, channel, group.isBlocked());
                }
            }
        PreferenceGroup groupPrefGroup = mPreference.findPreference(group.getId());
        if (groupPrefGroup != null) {
            updateGroupPreferences(group, groupPrefGroup);
        }
    }
}
+12 −0
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Switch;

import androidx.annotation.Keep;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceViewHolder;

import com.android.settings.R;
@@ -101,6 +103,16 @@ public class MasterSwitchPreference extends RestrictedPreference {
        return mSwitch != null && mChecked;
    }

    /**
     * Used to validate the state of mChecked and mCheckedSet when testing, without requiring
     * that a ViewHolder be bound to the object.
     */
    @Keep
    @Nullable
    public Boolean getCheckedState() {
        return mCheckedSet ? mChecked : null;
    }

    public void setChecked(boolean checked) {
        // Always set checked the first time; don't assume the field's default of false.
        final boolean changed = mChecked != checked;