Merge "Fix translation for device state notifications" into udc-dev
This commit is contained in:
@@ -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,69 +223,121 @@ class DeviceStateNotificationController extends BroadcastReceiver {
|
||||
mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
final SparseArray<NotificationInfo> notificationInfos = new SparseArray<>();
|
||||
private SparseArray<NotificationInfo> getNotificationInfos() {
|
||||
Locale locale = mContext.getResources().getConfiguration().getLocales().get(0);
|
||||
return mNotificationInfoProvider.getNotificationInfos(locale);
|
||||
}
|
||||
|
||||
final int[] stateIdentifiers =
|
||||
context.getResources().getIntArray(
|
||||
R.array.device_state_notification_state_identifiers);
|
||||
final String[] names =
|
||||
context.getResources().getStringArray(R.array.device_state_notification_names);
|
||||
final String[] activeNotificationTitles =
|
||||
context.getResources().getStringArray(
|
||||
R.array.device_state_notification_active_titles);
|
||||
final String[] activeNotificationContents =
|
||||
context.getResources().getStringArray(
|
||||
R.array.device_state_notification_active_contents);
|
||||
final String[] thermalCriticalNotificationTitles =
|
||||
context.getResources().getStringArray(
|
||||
R.array.device_state_notification_thermal_titles);
|
||||
final String[] thermalCriticalNotificationContents =
|
||||
context.getResources().getStringArray(
|
||||
R.array.device_state_notification_thermal_contents);
|
||||
final String[] powerSaveModeNotificationTitles =
|
||||
context.getResources().getStringArray(
|
||||
R.array.device_state_notification_power_save_titles);
|
||||
final String[] powerSaveModeNotificationContents =
|
||||
context.getResources().getStringArray(
|
||||
R.array.device_state_notification_power_save_contents);
|
||||
@VisibleForTesting
|
||||
public static class NotificationInfoProvider {
|
||||
@NonNull
|
||||
private final Context mContext;
|
||||
private final Object mLock = new Object();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@Nullable
|
||||
private SparseArray<NotificationInfo> mCachedNotificationInfos;
|
||||
|
||||
if (stateIdentifiers.length != names.length
|
||||
|| stateIdentifiers.length != activeNotificationTitles.length
|
||||
|| stateIdentifiers.length != activeNotificationContents.length
|
||||
|| stateIdentifiers.length != thermalCriticalNotificationTitles.length
|
||||
|| stateIdentifiers.length != thermalCriticalNotificationContents.length
|
||||
|| stateIdentifiers.length != powerSaveModeNotificationTitles.length
|
||||
|| stateIdentifiers.length != powerSaveModeNotificationContents.length
|
||||
) {
|
||||
throw new IllegalStateException(
|
||||
"The length of state identifiers and notification texts must match!");
|
||||
@GuardedBy("mLock")
|
||||
@Nullable
|
||||
@VisibleForTesting
|
||||
Locale mCachedLocale;
|
||||
|
||||
NotificationInfoProvider(@NonNull Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
for (int i = 0; i < stateIdentifiers.length; i++) {
|
||||
int identifier = stateIdentifiers[i];
|
||||
if (identifier == DeviceStateManager.INVALID_DEVICE_STATE) {
|
||||
continue;
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@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 =
|
||||
mContext.getResources().getIntArray(
|
||||
R.array.device_state_notification_state_identifiers);
|
||||
final String[] names =
|
||||
mContext.getResources().getStringArray(R.array.device_state_notification_names);
|
||||
final String[] activeNotificationTitles =
|
||||
mContext.getResources().getStringArray(
|
||||
R.array.device_state_notification_active_titles);
|
||||
final String[] activeNotificationContents =
|
||||
mContext.getResources().getStringArray(
|
||||
R.array.device_state_notification_active_contents);
|
||||
final String[] thermalCriticalNotificationTitles =
|
||||
mContext.getResources().getStringArray(
|
||||
R.array.device_state_notification_thermal_titles);
|
||||
final String[] thermalCriticalNotificationContents =
|
||||
mContext.getResources().getStringArray(
|
||||
R.array.device_state_notification_thermal_contents);
|
||||
final String[] powerSaveModeNotificationTitles =
|
||||
mContext.getResources().getStringArray(
|
||||
R.array.device_state_notification_power_save_titles);
|
||||
final String[] powerSaveModeNotificationContents =
|
||||
mContext.getResources().getStringArray(
|
||||
R.array.device_state_notification_power_save_contents);
|
||||
|
||||
if (stateIdentifiers.length != names.length
|
||||
|| stateIdentifiers.length != activeNotificationTitles.length
|
||||
|| stateIdentifiers.length != activeNotificationContents.length
|
||||
|| stateIdentifiers.length != thermalCriticalNotificationTitles.length
|
||||
|| stateIdentifiers.length != thermalCriticalNotificationContents.length
|
||||
|| stateIdentifiers.length != powerSaveModeNotificationTitles.length
|
||||
|| stateIdentifiers.length != powerSaveModeNotificationContents.length
|
||||
) {
|
||||
throw new IllegalStateException(
|
||||
"The length of state identifiers and notification texts must match!");
|
||||
}
|
||||
|
||||
notificationInfos.put(
|
||||
identifier,
|
||||
new NotificationInfo(
|
||||
names[i], activeNotificationTitles[i], activeNotificationContents[i],
|
||||
thermalCriticalNotificationTitles[i],
|
||||
thermalCriticalNotificationContents[i],
|
||||
powerSaveModeNotificationTitles[i],
|
||||
powerSaveModeNotificationContents[i])
|
||||
);
|
||||
}
|
||||
for (int i = 0; i < stateIdentifiers.length; i++) {
|
||||
int identifier = stateIdentifiers[i];
|
||||
if (identifier == DeviceStateManager.INVALID_DEVICE_STATE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return notificationInfos;
|
||||
notificationInfos.put(
|
||||
identifier,
|
||||
new NotificationInfo(
|
||||
names[i],
|
||||
activeNotificationTitles[i],
|
||||
activeNotificationContents[i],
|
||||
thermalCriticalNotificationTitles[i],
|
||||
thermalCriticalNotificationContents[i],
|
||||
powerSaveModeNotificationTitles[i],
|
||||
powerSaveModeNotificationContents[i])
|
||||
);
|
||||
}
|
||||
return notificationInfos;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user