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

Commit dcd1689a authored by Jiaming Liu's avatar Jiaming Liu Committed by Android (Google) Code Review
Browse files

Merge "Fix translation for device state notifications" into udc-dev

parents a4b9ab40 ca84d3d6
Loading
Loading
Loading
Loading
+121 −67
Original line number Diff line number Diff line
@@ -37,8 +37,11 @@ import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.util.Locale;

/**
 * Manages the user-visible device state notifications.
 */
@@ -56,15 +59,14 @@ class DeviceStateNotificationController extends BroadcastReceiver {
    private final NotificationManager mNotificationManager;
    private final PackageManager mPackageManager;

    // Stores the notification title and content indexed with the device state identifier.
    private final SparseArray<NotificationInfo> mNotificationInfos;

    // The callback when a device state is requested to be canceled.
    private final Runnable mCancelStateRunnable;

    private final NotificationInfoProvider mNotificationInfoProvider;

    DeviceStateNotificationController(@NonNull Context context, @NonNull Handler handler,
            @NonNull Runnable cancelStateRunnable) {
        this(context, handler, cancelStateRunnable, getNotificationInfos(context),
        this(context, handler, cancelStateRunnable, new NotificationInfoProvider(context),
                context.getPackageManager(), context.getSystemService(NotificationManager.class));
    }

@@ -72,13 +74,13 @@ class DeviceStateNotificationController extends BroadcastReceiver {
    DeviceStateNotificationController(
            @NonNull Context context, @NonNull Handler handler,
            @NonNull Runnable cancelStateRunnable,
            @NonNull SparseArray<NotificationInfo> notificationInfos,
            @NonNull NotificationInfoProvider notificationInfoProvider,
            @NonNull PackageManager packageManager,
            @NonNull NotificationManager notificationManager) {
        mContext = context;
        mHandler = handler;
        mCancelStateRunnable = cancelStateRunnable;
        mNotificationInfos = notificationInfos;
        mNotificationInfoProvider = notificationInfoProvider;
        mPackageManager = packageManager;
        mNotificationManager = notificationManager;
        mContext.registerReceiver(
@@ -97,7 +99,7 @@ class DeviceStateNotificationController extends BroadcastReceiver {
     * @param requestingAppUid the uid of the requesting app used to retrieve the app name.
     */
    void showStateActiveNotificationIfNeeded(int state, int requestingAppUid) {
        NotificationInfo info = mNotificationInfos.get(state);
        NotificationInfo info = getNotificationInfos().get(state);
        if (info == null || !info.hasActiveNotification()) {
            return;
        }
@@ -127,7 +129,7 @@ class DeviceStateNotificationController extends BroadcastReceiver {
     * @param state the identifier of the device state being canceled.
     */
    void showThermalCriticalNotificationIfNeeded(int state) {
        NotificationInfo info = mNotificationInfos.get(state);
        NotificationInfo info = getNotificationInfos().get(state);
        if (info == null || !info.hasThermalCriticalNotification()) {
            return;
        }
@@ -148,7 +150,7 @@ class DeviceStateNotificationController extends BroadcastReceiver {
     * @param state the identifier of the device state being canceled.
     */
    void showPowerSaveNotificationIfNeeded(int state) {
        NotificationInfo info = mNotificationInfos.get(state);
        NotificationInfo info = getNotificationInfos().get(state);
        if (info == null || !info.hasPowerSaveModeNotification()) {
            return;
        }
@@ -170,7 +172,7 @@ class DeviceStateNotificationController extends BroadcastReceiver {
     * @param state the device state identifier.
     */
    void cancelNotification(int state) {
        if (!mNotificationInfos.contains(state)) {
        if (getNotificationInfos().get(state) == null) {
            return;
        }
        mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
@@ -221,39 +223,89 @@ class DeviceStateNotificationController extends BroadcastReceiver {
        mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
    }

    private SparseArray<NotificationInfo> getNotificationInfos() {
        Locale locale = mContext.getResources().getConfiguration().getLocales().get(0);
        return mNotificationInfoProvider.getNotificationInfos(locale);
    }

    @VisibleForTesting
    public static class NotificationInfoProvider {
        @NonNull
        private final Context mContext;
        private final Object mLock = new Object();

        @GuardedBy("mLock")
        @Nullable
        private SparseArray<NotificationInfo> mCachedNotificationInfos;

        @GuardedBy("mLock")
        @Nullable
        @VisibleForTesting
        Locale mCachedLocale;

        NotificationInfoProvider(@NonNull Context context) {
            mContext = context;
        }

        /**
         * Loads the resources for the notifications. The device state identifiers and strings are
         * stored in arrays. All the string arrays must have the same length and same order as the
         * identifier array.
         */
    private static SparseArray<NotificationInfo> getNotificationInfos(Context context) {
        @NonNull
        public SparseArray<NotificationInfo> getNotificationInfos(@NonNull Locale locale) {
            synchronized (mLock) {
                if (!locale.equals(mCachedLocale)) {
                    refreshNotificationInfos(locale);
                }
                return mCachedNotificationInfos;
            }
        }


        @VisibleForTesting
        Locale getCachedLocale() {
            synchronized (mLock) {
                return mCachedLocale;
            }
        }

        @VisibleForTesting
        public void refreshNotificationInfos(Locale locale) {
            synchronized (mLock) {
                mCachedLocale = locale;
                mCachedNotificationInfos = loadNotificationInfos();
            }
        }

        @VisibleForTesting
        public SparseArray<NotificationInfo> loadNotificationInfos() {
            final SparseArray<NotificationInfo> notificationInfos = new SparseArray<>();

            final int[] stateIdentifiers =
                context.getResources().getIntArray(
                    mContext.getResources().getIntArray(
                            R.array.device_state_notification_state_identifiers);
            final String[] names =
                context.getResources().getStringArray(R.array.device_state_notification_names);
                    mContext.getResources().getStringArray(R.array.device_state_notification_names);
            final String[] activeNotificationTitles =
                context.getResources().getStringArray(
                    mContext.getResources().getStringArray(
                            R.array.device_state_notification_active_titles);
            final String[] activeNotificationContents =
                context.getResources().getStringArray(
                    mContext.getResources().getStringArray(
                            R.array.device_state_notification_active_contents);
            final String[] thermalCriticalNotificationTitles =
                context.getResources().getStringArray(
                    mContext.getResources().getStringArray(
                            R.array.device_state_notification_thermal_titles);
            final String[] thermalCriticalNotificationContents =
                context.getResources().getStringArray(
                    mContext.getResources().getStringArray(
                            R.array.device_state_notification_thermal_contents);
            final String[] powerSaveModeNotificationTitles =
                context.getResources().getStringArray(
                    mContext.getResources().getStringArray(
                            R.array.device_state_notification_power_save_titles);
            final String[] powerSaveModeNotificationContents =
                context.getResources().getStringArray(
                    mContext.getResources().getStringArray(
                            R.array.device_state_notification_power_save_contents);


            if (stateIdentifiers.length != names.length
                    || stateIdentifiers.length != activeNotificationTitles.length
                    || stateIdentifiers.length != activeNotificationContents.length
@@ -275,16 +327,18 @@ class DeviceStateNotificationController extends BroadcastReceiver {
                notificationInfos.put(
                        identifier,
                        new NotificationInfo(
                            names[i], activeNotificationTitles[i], activeNotificationContents[i],
                                names[i],
                                activeNotificationTitles[i],
                                activeNotificationContents[i],
                                thermalCriticalNotificationTitles[i],
                                thermalCriticalNotificationContents[i],
                                powerSaveModeNotificationTitles[i],
                                powerSaveModeNotificationContents[i])
                );
            }

            return notificationInfos;
        }
    }

    /**
     * A helper function to get app name (label) using the app uid.
+37 −1
Original line number Diff line number Diff line
@@ -17,8 +17,13 @@
package com.android.server.devicestate;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -41,6 +46,8 @@ import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;

import java.util.Locale;

/**
 * Unit tests for {@link DeviceStateNotificationController}.
 * <p/>
@@ -77,6 +84,8 @@ public class DeviceStateNotificationControllerTest {
            Notification.class);
    private final NotificationManager mNotificationManager = mock(NotificationManager.class);

    private DeviceStateNotificationController.NotificationInfoProvider mNotificationInfoProvider;

    @Before
    public void setup() throws Exception {
        Context context = InstrumentationRegistry.getInstrumentation().getContext();
@@ -97,6 +106,11 @@ public class DeviceStateNotificationControllerTest {
                        THERMAL_TITLE_2, THERMAL_CONTENT_2,
                        POWER_SAVE_TITLE_2, POWER_SAVE_CONTENT_2));

        mNotificationInfoProvider =
                new DeviceStateNotificationController.NotificationInfoProvider(context);
        mNotificationInfoProvider = spy(mNotificationInfoProvider);
        doReturn(notificationInfos).when(mNotificationInfoProvider).loadNotificationInfos();

        when(packageManager.getNameForUid(VALID_APP_UID)).thenReturn(VALID_APP_NAME);
        when(packageManager.getNameForUid(INVALID_APP_UID)).thenReturn(INVALID_APP_NAME);
        when(packageManager.getApplicationInfo(eq(VALID_APP_NAME), ArgumentMatchers.any()))
@@ -106,7 +120,7 @@ public class DeviceStateNotificationControllerTest {
        when(applicationInfo.loadLabel(eq(packageManager))).thenReturn(VALID_APP_LABEL);

        mController = new DeviceStateNotificationController(
                context, handler, cancelStateRunnable, notificationInfos,
                context, handler, cancelStateRunnable, mNotificationInfoProvider,
                packageManager, mNotificationManager);
    }

@@ -223,4 +237,26 @@ public class DeviceStateNotificationControllerTest {
                eq(DeviceStateNotificationController.NOTIFICATION_ID),
                mNotificationCaptor.capture());
    }

    @Test
    public void test_notificationInfoProvider() {
        assertNull(mNotificationInfoProvider.getCachedLocale());

        mNotificationInfoProvider.getNotificationInfos(Locale.ENGLISH);
        verify(mNotificationInfoProvider).refreshNotificationInfos(eq(Locale.ENGLISH));
        assertEquals(Locale.ENGLISH, mNotificationInfoProvider.getCachedLocale());
        clearInvocations(mNotificationInfoProvider);

        // If the same locale is used again, the provider uses the cached value, so it won't refresh
        mNotificationInfoProvider.getNotificationInfos(Locale.ENGLISH);
        verify(mNotificationInfoProvider, never()).refreshNotificationInfos(eq(Locale.ENGLISH));
        assertEquals(Locale.ENGLISH, mNotificationInfoProvider.getCachedLocale());
        clearInvocations(mNotificationInfoProvider);

        // If a different locale is used, the provider refreshes.
        mNotificationInfoProvider.getNotificationInfos(Locale.ITALY);
        verify(mNotificationInfoProvider).refreshNotificationInfos(eq(Locale.ITALY));
        assertEquals(Locale.ITALY, mNotificationInfoProvider.getCachedLocale());
        clearInvocations(mNotificationInfoProvider);
    }
}