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

Commit d5c0851c authored by Yuri Lin's avatar Yuri Lin
Browse files

Add setting for promoted (live) notifications

Add a switch to control whether live notifications are permitted for an app, at the top of the app's notification settings page. This controls canPostPromotedNotifications() for that app.

Bug: 369150427
Flag: android.app.ui_rich_ongoing
Test: manual, PromotedNotificationsPreferenceControllerTest
Change-Id: I3a7c05721f1e879fb5fb0f5fb5f283dedbd195da
parent b514c11c
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -8764,6 +8764,13 @@
    <!-- App Info > Notifications: Title for section where notifications bundles can be configured [CHAR LIMIT=80]-->
    <string name="notification_bundles">Notification bundles</string>
    <!-- App Info > Notifications: Title for section controlling whether this app may show notifications in a promoted format [CHAR LIMIT=80] -->
    <string name="live_notifications">Live notifications</string>
    <!-- App Info > Notifications: Text on the switch to enable or disable an app from showing notifications in a live/promoted format [CHAR LIMIT=50] -->
    <string name="live_notifications_switch">Show live info</string>
    <!-- App Info > Notifications: Text accompanying the "Show live info" switch explaining the purpose of the setting -->
    <string name="live_notifications_desc">Pinned notifications display live info from apps, and always appear on the status bar and lock screen</string>
    <!-- Configure Notifications: Title for the notification bubbles option. [CHAR LIMIT=60] -->
    <string name="notification_bubbles_title">Bubbles</string>
    <!-- Title for the toggle shown on the app-level bubbles page  [CHAR LIMIT=60] -->
+17 −0
Original line number Diff line number Diff line
@@ -30,6 +30,23 @@
    <Preference
        android:key="block_desc" />

    <!-- Whether the app can show promoted notifications -->
    <PreferenceCategory
        android:title="@string/live_notifications"
        android:key="promoted_category"
        android:visibility="gone">

        <com.android.settingslib.RestrictedSwitchPreference
            android:key="promoted_switch"
            android:title="@string/live_notifications_switch" />

        <Preference
            android:key="promoted_desc"
            android:summary="@string/live_notifications_desc"
            android:selectable="false"/>

    </PreferenceCategory>

    <!-- Conversations added here -->
    <PreferenceCategory
        android:title="@string/conversations_category_title"
+34 −2
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY

import static com.android.server.notification.Flags.notificationHideUnusedChannels;

import android.app.Flags;
import android.app.INotificationManager;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -66,11 +67,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class NotificationBackend {
    private static final String TAG = "NotificationBackend";
@@ -102,6 +101,9 @@ public class NotificationBackend {
        row.blockedChannelCount = getBlockedChannelCount(row.pkg, row.uid);
        row.channelCount = getChannelCount(row.pkg, row.uid);
        recordAggregatedUsageEvents(context, row);
        if (Flags.uiRichOngoing()) {
            row.canBePromoted = canBePromoted(row.pkg, row.uid);
        }
        return row;
    }

@@ -707,6 +709,35 @@ public class NotificationBackend {
        }
    }

    /**
     * Retrieves whether the app with given package and uid is permitted to post promoted
     * notifications.
     */
    public boolean canBePromoted(String pkg, int uid) {
        try {
            return sINM.appCanBePromoted(pkg, uid);
        } catch (Exception e) {
            Log.w(TAG, "Error calling NoMan", e);
            return false;
        }
    }

    /**
     * Sets whether the app with given package and uid is permitted to post promoted notifications.
     */
    public void setCanBePromoted(String pkg, int uid, boolean allowed) {
        // We shouldn't get here with the flag off, but just in case, do nothing.
        if (!Flags.uiRichOngoing()) {
            Log.wtf(TAG, "tried to setCanBePromoted without flag on");
            return;
        }
        try {
            sINM.setCanBePromoted(pkg, uid, allowed, /* fromUser= */ true);
        } catch (Exception e) {
            Log.w(TAG, "Error calling NoMan", e);
        }
    }

    @VisibleForTesting
    void setNm(INotificationManager inm) {
        sINM = inm;
@@ -748,5 +779,6 @@ public class NotificationBackend {
        public Map<String, NotificationsSentState> sentByChannel;
        public NotificationsSentState sentByApp;
        public boolean showAllChannels = true;
        public boolean canBePromoted;
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -104,6 +104,7 @@ public class AppNotificationSettings extends NotificationSettings {
        mControllers.add(new ShowMorePreferenceController(
                context, mDependentFieldListener, mBackend));
        mControllers.add(new BundleListPreferenceController(context, mBackend));
        mControllers.add(new PromotedNotificationsPreferenceController(context, mBackend));
        return new ArrayList<>(mControllers);
    }
}
+83 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.app;

import android.app.Flags;
import android.content.Context;

import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;

import com.android.settings.notification.NotificationBackend;
import com.android.settingslib.RestrictedSwitchPreference;

public class PromotedNotificationsPreferenceController extends
        NotificationPreferenceController implements Preference.OnPreferenceChangeListener {
    private static final String KEY_PROMOTED_CATEGORY = "promoted_category";
    private static final String KEY_PROMOTED_SWITCH = "promoted_switch";

    public PromotedNotificationsPreferenceController(@NonNull Context context,
            @NonNull NotificationBackend backend) {
        super(context, backend);
    }

    @Override
    @NonNull
    public String getPreferenceKey() {
        return KEY_PROMOTED_CATEGORY;
    }

    @Override
    public boolean isAvailable() {
        if (!Flags.uiRichOngoing()) {
            return false;
        }
        return super.isAvailable();
    }

    @Override
    boolean isIncludedInFilter() {
        // not a channel-specific preference; only at the app level
        return false;
    }

    /**
     * Updates the state of the promoted notifications switch. Because this controller governs
     * the full PreferenceCategory, we must find the switch preference within the category first.
     */
    public void updateState(@NonNull Preference preference) {
        PreferenceCategory category = (PreferenceCategory) preference;
        RestrictedSwitchPreference pref = category.findPreference(KEY_PROMOTED_SWITCH);

        if (pref != null && mAppRow != null) {
            pref.setDisabledByAdmin(mAdmin);
            pref.setEnabled(!pref.isDisabledByAdmin());
            pref.setChecked(mAppRow.canBePromoted);
            pref.setOnPreferenceChangeListener(this);
        }
    }

    @Override
    public boolean onPreferenceChange(@NonNull Preference preference, @NonNull Object newValue) {
        final boolean canBePromoted = (Boolean) newValue;
        if (mAppRow != null && mAppRow.canBePromoted != canBePromoted) {
            mAppRow.canBePromoted = canBePromoted;
            mBackend.setCanBePromoted(mAppRow.pkg, mAppRow.uid, canBePromoted);
        }
        return true;
    }
}
Loading