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

Commit ab1217ce authored by Jiaming Liu's avatar Jiaming Liu Committed by Automerger Merge Worker
Browse files

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

parents df60c2e8 dcd1689a
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);
    }
}