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

Commit 93b77e30 authored by Jernej Virag's avatar Jernej Virag Committed by Android (Google) Code Review
Browse files

Merge "Reduce needless recoverBuilder calls when updating Notification icons" into main

parents ba822b99 34376809
Loading
Loading
Loading
Loading
+97 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.systemui.statusbar.notification

import android.app.Notification
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class NotificationContentDescriptionTest : SysuiTestCase() {

    private val TITLE = "this is a title"
    private val TEXT = "this is text"
    private val TICKER = "this is a ticker"

    @Test
    fun notificationWithAllDifferentFields_descriptionIsTitle() {
        val n = createNotification(TITLE, TEXT, TICKER)
        val description = contentDescForNotification(context, n)
        assertThat(description).isEqualTo(createDescriptionText(n, TITLE))
    }

    @Test
    fun notificationWithAllDifferentFields_titleMatchesAppName_descriptionIsText() {
        val n = createNotification(getTestAppName(), TEXT, TICKER)
        val description = contentDescForNotification(context, n)
        assertThat(description).isEqualTo(createDescriptionText(n, TEXT))
    }

    @Test
    fun notificationWithAllDifferentFields_titleMatchesAppNameNoText_descriptionIsTicker() {
        val n = createNotification(getTestAppName(), null, TICKER)
        val description = contentDescForNotification(context, n)
        assertThat(description).isEqualTo(createDescriptionText(n, TICKER))
    }

    @Test
    fun notificationWithAllDifferentFields_titleMatchesAppNameNoTextNoTicker_descriptionEmpty() {
        val appName = getTestAppName()
        val n = createNotification(appName, null, null)
        val description = contentDescForNotification(context, n)
        assertThat(description).isEqualTo(createDescriptionText(n, ""))
    }

    @Test
    fun nullNotification_descriptionIsAppName() {
        val description = contentDescForNotification(context, null)
        assertThat(description).isEqualTo(createDescriptionText(null, ""))
    }

    private fun createNotification(
        title: String? = null,
        text: String? = null,
        ticker: String? = null
    ): Notification =
        Notification.Builder(context)
            .setContentTitle(title)
            .setContentText(text)
            .setTicker(ticker)
            .build()

    private fun getTestAppName(): String {
        return getAppName(createNotification("", "", ""))
    }

    private fun getAppName(n: Notification?) =
        n?.let {
            val builder = Notification.Builder.recoverBuilder(context, it)
            builder.loadHeaderAppName()
        }
            ?: ""

    private fun createDescriptionText(n: Notification?, desc: String?): String {
        val appName = getAppName(n)
        return context.getString(R.string.accessibility_desc_notification_icon, appName, desc)
    }
}
+16 −41
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -59,6 +58,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.NotificationContentDescription;
import com.android.systemui.statusbar.notification.NotificationDozeHelper;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
@@ -350,9 +350,22 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
    }

    public void setNotification(StatusBarNotification notification) {
        mNotification = notification;
        CharSequence contentDescription = null;
        if (notification != null) {
            setContentDescription(notification.getNotification());
            contentDescription = NotificationContentDescription
                    .contentDescForNotification(mContext, notification.getNotification());
        }
        setNotification(notification, contentDescription);
    }

    /**
     * Sets the notification with a pre-set content description.
     */
    public void setNotification(@Nullable StatusBarNotification notification,
            @Nullable CharSequence notificationContentDescription) {
        mNotification = notification;
        if (!TextUtils.isEmpty(notificationContentDescription)) {
            setContentDescription(notificationContentDescription);
        }
        maybeUpdateIconScaleDimens();
    }
@@ -621,15 +634,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
        mNumberBackground.setBounds(w-dw, h-dh, w, h);
    }

    private void setContentDescription(Notification notification) {
        if (notification != null) {
            String d = contentDescForNotification(mContext, notification);
            if (!TextUtils.isEmpty(d)) {
                setContentDescription(d);
            }
        }
    }

    @Override
    public String toString() {
        return "StatusBarIconView("
@@ -647,35 +651,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
        return mSlot;
    }


    public static String contentDescForNotification(Context c, Notification n) {
        String appName = "";
        try {
            Notification.Builder builder = Notification.Builder.recoverBuilder(c, n);
            appName = builder.loadHeaderAppName();
        } catch (RuntimeException e) {
            Log.e(TAG, "Unable to recover builder", e);
            // Trying to get the app name from the app info instead.
            ApplicationInfo appInfo = n.extras.getParcelable(
                    Notification.EXTRA_BUILDER_APPLICATION_INFO, ApplicationInfo.class);
            if (appInfo != null) {
                appName = String.valueOf(appInfo.loadLabel(c.getPackageManager()));
            }
        }

        CharSequence title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
        CharSequence text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
        CharSequence ticker = n.tickerText;

        // Some apps just put the app name into the title
        CharSequence titleOrText = TextUtils.equals(title, appName) ? text : title;

        CharSequence desc = !TextUtils.isEmpty(titleOrText) ? titleOrText
                : !TextUtils.isEmpty(ticker) ? ticker : "";

        return c.getString(R.string.accessibility_desc_notification_icon, appName, desc);
    }

    /**
     * Set the color that is used to draw decoration like the overflow dot. This will not be applied
     * to the drawable.
+62 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */

@file:JvmName("NotificationContentDescription")

package com.android.systemui.statusbar.notification

import android.app.Notification
import android.content.Context
import android.content.pm.ApplicationInfo
import android.text.TextUtils
import android.util.Log
import androidx.annotation.MainThread
import com.android.systemui.res.R

/**
 * Returns accessibility content description for a given notification.
 *
 * NOTE: This is a relatively slow call.
 */
@MainThread
fun contentDescForNotification(c: Context, n: Notification?): CharSequence {
    var appName = ""
    try {
        val builder = Notification.Builder.recoverBuilder(c, n)
        appName = builder.loadHeaderAppName()
    } catch (e: RuntimeException) {
        Log.e("ContentDescription", "Unable to recover builder", e)
        // Trying to get the app name from the app info instead.
        val appInfo =
            n?.extras?.getParcelable(
                Notification.EXTRA_BUILDER_APPLICATION_INFO,
                ApplicationInfo::class.java
            )
        if (appInfo != null) {
            appName = appInfo.loadLabel(c.packageManager).toString()
        }
    }
    val title = n?.extras?.getCharSequence(Notification.EXTRA_TITLE)
    val text = n?.extras?.getCharSequence(Notification.EXTRA_TEXT)
    val ticker = n?.tickerText

    // Some apps just put the app name into the title
    val titleOrText = if (TextUtils.equals(title, appName)) text else title
    val desc =
        if (!TextUtils.isEmpty(titleOrText)) titleOrText
        else if (!TextUtils.isEmpty(ticker)) ticker else ""
    return c.getString(R.string.accessibility_desc_notification_icon, appName, desc)
}
+2 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.app.Notification
import android.content.Context
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.contentDescForNotification
import javax.inject.Inject

/**
@@ -36,6 +37,6 @@ class IconBuilder @Inject constructor(
    }

    fun getIconContentDescription(n: Notification): CharSequence {
        return StatusBarIconView.contentDescForNotification(context, n)
        return contentDescForNotification(context, n)
    }
}
+9 −6
Original line number Diff line number Diff line
@@ -26,15 +26,15 @@ import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ImageView
import com.android.app.tracing.traceSection
import com.android.internal.statusbar.StatusBarIcon
import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.InflationException
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.app.tracing.traceSection
import javax.inject.Inject

/**
@@ -153,24 +153,27 @@ class IconManager @Inject constructor(
        entry.icons.peopleAvatarDescriptor = null

        val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
        val notificationContentDescription = entry.sbn.notification?.let {
            iconBuilder.getIconContentDescription(it)
        }

        entry.icons.statusBarIcon?.let {
            it.notification = entry.sbn
            it.setNotification(entry.sbn, notificationContentDescription)
            setIcon(entry, normalIconDescriptor, it)
        }

        entry.icons.shelfIcon?.let {
            it.notification = entry.sbn
            it.setNotification(entry.sbn, notificationContentDescription)
            setIcon(entry, normalIconDescriptor, it)
        }

        entry.icons.aodIcon?.let {
            it.notification = entry.sbn
            it.setNotification(entry.sbn, notificationContentDescription)
            setIcon(entry, sensitiveIconDescriptor, it)
        }

        entry.icons.centeredIcon?.let {
            it.notification = entry.sbn
            it.setNotification(entry.sbn, notificationContentDescription)
            setIcon(entry, sensitiveIconDescriptor, it)
        }
    }
Loading