diff options
6 files changed, 192 insertions, 122 deletions
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml index c4bb17cb01dc..3f86aba26a1f 100644 --- a/packages/CarrierDefaultApp/AndroidManifest.xml +++ b/packages/CarrierDefaultApp/AndroidManifest.xml @@ -77,6 +77,7 @@ <receiver android:name="com.android.carrierdefaultapp.SlicePurchaseBroadcastReceiver" android:exported="true"> <intent-filter> + <action android:name="android.intent.action.LOCALE_CHANGED" /> <action android:name="com.android.phone.slice.action.START_SLICE_PURCHASE_APP" /> <action android:name="com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_TIMEOUT" /> <action android:name="com.android.phone.slice.action.NOTIFICATION_CANCELED" /> diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java index c524037fe444..c8bc771aca26 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java @@ -19,7 +19,6 @@ package com.android.carrierdefaultapp; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; -import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -81,8 +80,7 @@ public class SlicePurchaseActivity extends Activity { + ", url=" + mUrl); // Cancel network boost notification - mApplicationContext.getSystemService(NotificationManager.class) - .cancel(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG, mCapability); + SlicePurchaseBroadcastReceiver.cancelNotification(mApplicationContext, mCapability); // Verify intent and values are valid if (!SlicePurchaseBroadcastReceiver.isIntentValid(mIntent)) { @@ -113,9 +111,6 @@ public class SlicePurchaseActivity extends Activity { return; } - // Create a reference to this activity in SlicePurchaseBroadcastReceiver - SlicePurchaseBroadcastReceiver.updateSlicePurchaseActivity(mCapability, this); - // Create and configure WebView setupWebView(); } @@ -161,7 +156,6 @@ public class SlicePurchaseActivity extends Activity { logd("onDestroy: User canceled the purchase by closing the application."); SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse( mIntent, SlicePurchaseController.EXTRA_INTENT_CANCELED); - SlicePurchaseBroadcastReceiver.removeSlicePurchaseActivity(mCapability); super.onDestroy(); } diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java index 367ae06adfc7..3cc2a55421aa 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java @@ -24,7 +24,11 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.drawable.Icon; +import android.os.LocaleList; +import android.os.SystemProperties; import android.os.UserHandle; import android.telephony.AnomalyReporter; import android.telephony.SubscriptionManager; @@ -36,8 +40,8 @@ import android.webkit.WebView; import com.android.internal.annotations.VisibleForTesting; import com.android.phone.slice.SlicePurchaseController; -import java.lang.ref.WeakReference; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.UUID; @@ -57,10 +61,6 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{ */ private static final String UUID_BAD_PENDING_INTENT = "c360246e-95dc-4abf-9dc1-929a76cd7e53"; - /** Weak references to {@link SlicePurchaseActivity} for each capability, if it exists. */ - private static final Map<Integer, WeakReference<SlicePurchaseActivity>> - sSlicePurchaseActivities = new HashMap<>(); - /** Channel ID for the network boost notification. */ private static final String NETWORK_BOOST_NOTIFICATION_CHANNEL_ID = "network_boost"; /** Tag for the network boost notification. */ @@ -70,27 +70,28 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{ "com.android.phone.slice.action.NOTIFICATION_CANCELED"; /** - * Create a weak reference to {@link SlicePurchaseActivity}. The reference will be removed when - * {@link SlicePurchaseActivity#onDestroy()} is called. - * - * @param capability The premium capability requested. - * @param slicePurchaseActivity The instance of SlicePurchaseActivity. + * A map of Intents sent by {@link SlicePurchaseController} for each capability. + * If this map contains an Intent for a given capability, the network boost notification to + * purchase the capability is visible to the user. + * If this map does not contain an Intent for a given capability, either the capability was + * never requested or the {@link SlicePurchaseActivity} is visible to the user. + * An Intent is added to this map when the network boost notification is displayed to the user + * and removed from the map when the notification is canceled. */ - public static void updateSlicePurchaseActivity( - @TelephonyManager.PremiumCapability int capability, - @NonNull SlicePurchaseActivity slicePurchaseActivity) { - sSlicePurchaseActivities.put(capability, new WeakReference<>(slicePurchaseActivity)); - } + private static final Map<Integer, Intent> sIntents = new HashMap<>(); /** - * Remove the weak reference to {@link SlicePurchaseActivity} when - * {@link SlicePurchaseActivity#onDestroy()} is called. + * Cancel the network boost notification for the given capability and + * remove the corresponding notification intent from the map. * - * @param capability The premium capability requested. + * @param context The context to cancel the notification in. + * @param capability The premium capability to cancel the notification for. */ - public static void removeSlicePurchaseActivity( + public static void cancelNotification(@NonNull Context context, @TelephonyManager.PremiumCapability int capability) { - sSlicePurchaseActivities.remove(capability); + context.getSystemService(NotificationManager.class).cancelAsUser( + NETWORK_BOOST_NOTIFICATION_TAG, capability, UserHandle.ALL); + sIntents.remove(capability); } /** @@ -223,8 +224,11 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{ public void onReceive(@NonNull Context context, @NonNull Intent intent) { logd("onReceive intent: " + intent.getAction()); switch (intent.getAction()) { + case Intent.ACTION_LOCALE_CHANGED: + onLocaleChanged(context); + break; case SlicePurchaseController.ACTION_START_SLICE_PURCHASE_APP: - onDisplayNetworkBoostNotification(context, intent); + onDisplayNetworkBoostNotification(context, intent, false); break; case SlicePurchaseController.ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT: onTimeout(context, intent); @@ -237,17 +241,31 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{ } } + private void onLocaleChanged(@NonNull Context context) { + if (sIntents.isEmpty()) return; + + for (int capability : new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY}) { + if (sIntents.get(capability) != null) { + // Notification is active -- update notification for new locale + context.getSystemService(NotificationManager.class).cancelAsUser( + NETWORK_BOOST_NOTIFICATION_TAG, capability, UserHandle.ALL); + onDisplayNetworkBoostNotification(context, sIntents.get(capability), true); + } + } + } + private void onDisplayNetworkBoostNotification(@NonNull Context context, - @NonNull Intent intent) { - if (!isIntentValid(intent)) { + @NonNull Intent intent, boolean repeat) { + if (!repeat && !isIntentValid(intent)) { sendSlicePurchaseAppResponse(intent, SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED); return; } + Resources res = getResources(context); NotificationChannel channel = new NotificationChannel( NETWORK_BOOST_NOTIFICATION_CHANNEL_ID, - context.getResources().getString(R.string.network_boost_notification_channel), + res.getString(R.string.network_boost_notification_channel), NotificationManager.IMPORTANCE_DEFAULT); // CarrierDefaultApp notifications are unblockable by default. Make this channel blockable // to allow users to disable notifications posted to this channel without affecting other @@ -257,12 +275,11 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{ Notification notification = new Notification.Builder(context, NETWORK_BOOST_NOTIFICATION_CHANNEL_ID) - .setContentTitle(String.format(context.getResources().getString( + .setContentTitle(String.format(res.getString( R.string.network_boost_notification_title), intent.getStringExtra( SlicePurchaseController.EXTRA_REQUESTING_APP_NAME))) - .setContentText(context.getResources().getString( - R.string.network_boost_notification_detail)) + .setContentText(res.getString(R.string.network_boost_notification_detail)) .setSmallIcon(R.drawable.ic_network_boost) .setContentIntent(createContentIntent(context, intent, 1)) .setDeleteIntent(intent.getParcelableExtra( @@ -271,26 +288,56 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{ // the user canceling or closing the notification. .addAction(new Notification.Action.Builder( Icon.createWithResource(context, R.drawable.ic_network_boost), - context.getResources().getString( - R.string.network_boost_notification_button_not_now), + res.getString(R.string.network_boost_notification_button_not_now), createCanceledIntent(context, intent)).build()) // Add an action for the "Manage" button, which has the same behavior as // the user clicking on the notification. .addAction(new Notification.Action.Builder( Icon.createWithResource(context, R.drawable.ic_network_boost), - context.getResources().getString( - R.string.network_boost_notification_button_manage), + res.getString(R.string.network_boost_notification_button_manage), createContentIntent(context, intent, 2)).build()) .build(); int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY, SlicePurchaseController.PREMIUM_CAPABILITY_INVALID); - logd("Display the network boost notification for capability " + logd((repeat ? "Update" : "Display") + " the network boost notification for capability " + TelephonyManager.convertPremiumCapabilityToString(capability)); context.getSystemService(NotificationManager.class).notifyAsUser( NETWORK_BOOST_NOTIFICATION_TAG, capability, notification, UserHandle.ALL); - sendSlicePurchaseAppResponse(intent, - SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN); + if (!repeat) { + sIntents.put(capability, intent); + sendSlicePurchaseAppResponse(intent, + SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN); + } + } + + /** + * Get the {@link Resources} for the current locale. + * + * @param context The context to get the resources in. + * + * @return The resources in the current locale. + */ + @VisibleForTesting + @NonNull public Resources getResources(@NonNull Context context) { + Resources resources = context.getResources(); + Configuration config = resources.getConfiguration(); + config.setLocale(getCurrentLocale()); + return new Resources(resources.getAssets(), resources.getDisplayMetrics(), config); + } + + /** + * Get the current {@link Locale} from the system property {@code persist.sys.locale}. + * + * @return The user's default/preferred language. + */ + @VisibleForTesting + @NonNull public Locale getCurrentLocale() { + String languageTag = SystemProperties.get("persist.sys.locale"); + if (TextUtils.isEmpty(languageTag)) { + return LocaleList.getAdjustedDefault().get(0); + } + return Locale.forLanguageTag(languageTag); } /** @@ -343,16 +390,13 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{ SlicePurchaseController.PREMIUM_CAPABILITY_INVALID); logd("Purchase capability " + TelephonyManager.convertPremiumCapabilityToString(capability) + " timed out."); - if (sSlicePurchaseActivities.get(capability) == null) { - // Notification is still active + if (sIntents.get(capability) != null) { + // Notification is still active -- cancel pending notification logd("Closing network boost notification since the user did not respond in time."); - context.getSystemService(NotificationManager.class).cancelAsUser( - NETWORK_BOOST_NOTIFICATION_TAG, capability, UserHandle.ALL); + cancelNotification(context, capability); } else { - // Notification was dismissed but SlicePurchaseActivity is still active - logd("Closing slice purchase application WebView since the user did not complete the " - + "purchase in time."); - sSlicePurchaseActivities.get(capability).get().finishAndRemoveTask(); + // SlicePurchaseActivity is still active -- ignore timer + logd("Ignoring timeout since the SlicePurchaseActivity is still active."); } } @@ -360,8 +404,7 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{ int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY, SlicePurchaseController.PREMIUM_CAPABILITY_INVALID); logd("onUserCanceled: " + TelephonyManager.convertPremiumCapabilityToString(capability)); - context.getSystemService(NotificationManager.class) - .cancelAsUser(NETWORK_BOOST_NOTIFICATION_TAG, capability, UserHandle.ALL); + cancelNotification(context, capability); sendSlicePurchaseAppResponse(intent, SlicePurchaseController.EXTRA_INTENT_CANCELED); } diff --git a/packages/CarrierDefaultApp/tests/unit/Android.bp b/packages/CarrierDefaultApp/tests/unit/Android.bp index cdf795752e07..0d08ec6ca2b3 100644 --- a/packages/CarrierDefaultApp/tests/unit/Android.bp +++ b/packages/CarrierDefaultApp/tests/unit/Android.bp @@ -37,5 +37,6 @@ android_test { // Include all test java files. srcs: ["src/**/*.java"], platform_apis: true, + use_embedded_native_libs: false, instrumentation_for: "CarrierDefaultApp", } diff --git a/packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml b/packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml index 7a26d95551df..995170a61614 100644 --- a/packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml +++ b/packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml @@ -17,7 +17,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.carrierdefaultapp.tests.unit"> <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" /> - <application> + <application android:extractNativeLibs="true"> <uses-library android:name="android.test.runner" /> </application> diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java index ab99a76902d9..20ffb27d4294 100644 --- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java +++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java @@ -19,9 +19,12 @@ package com.android.carrierdefaultapp; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; @@ -35,11 +38,11 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.content.res.Resources; import android.os.UserHandle; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; -import android.util.DisplayMetrics; import androidx.test.runner.AndroidJUnit4; @@ -52,6 +55,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Locale; + @RunWith(AndroidJUnit4.class) public class SlicePurchaseBroadcastReceiverTest { private static final int PHONE_ID = 0; @@ -67,24 +72,26 @@ public class SlicePurchaseBroadcastReceiverTest { @Mock PendingIntent mNotificationShownIntent; @Mock Context mContext; @Mock Resources mResources; + @Mock Configuration mConfiguration; @Mock NotificationManager mNotificationManager; @Mock ApplicationInfo mApplicationInfo; @Mock PackageManager mPackageManager; - @Mock DisplayMetrics mDisplayMetrics; - @Mock SlicePurchaseActivity mSlicePurchaseActivity; private SlicePurchaseBroadcastReceiver mSlicePurchaseBroadcastReceiver; - private ArgumentCaptor<Intent> mIntentCaptor; - private ArgumentCaptor<Notification> mNotificationCaptor; + private Resources mSpiedResources; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mSpiedResources = spy(Resources.getSystem()); + + doReturn("").when(mResources).getString(anyInt()); doReturn(mNotificationManager).when(mContext) .getSystemService(eq(NotificationManager.class)); + doReturn(mApplicationInfo).when(mContext).getApplicationInfo(); + doReturn(mPackageManager).when(mContext).getPackageManager(); + doReturn(mSpiedResources).when(mContext).getResources(); - mIntentCaptor = ArgumentCaptor.forClass(Intent.class); - mNotificationCaptor = ArgumentCaptor.forClass(Notification.class); mSlicePurchaseBroadcastReceiver = spy(new SlicePurchaseBroadcastReceiver()); } @@ -109,8 +116,9 @@ public class SlicePurchaseBroadcastReceiverTest { eq(EXTRA), eq(PendingIntent.class)); SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData( mContext, mIntent, EXTRA, mDataIntent); - verify(mPendingIntent).send(eq(mContext), eq(0), mIntentCaptor.capture()); - assertEquals(mDataIntent, mIntentCaptor.getValue()); + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); + verify(mPendingIntent).send(eq(mContext), eq(0), captor.capture()); + assertEquals(mDataIntent, captor.getValue()); } @Test @@ -138,17 +146,29 @@ public class SlicePurchaseBroadcastReceiverTest { @Test public void testDisplayNetworkBoostNotification() throws Exception { - // set up intent - doReturn(SlicePurchaseController.ACTION_START_SLICE_PURCHASE_APP).when(mIntent).getAction(); - doReturn(PHONE_ID).when(mIntent).getIntExtra( - eq(SlicePurchaseController.EXTRA_PHONE_ID), anyInt()); - doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mIntent).getIntExtra( - eq(SlicePurchaseController.EXTRA_SUB_ID), anyInt()); - doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra( - eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt()); - doReturn(TAG).when(mIntent).getStringExtra( - eq(SlicePurchaseController.EXTRA_REQUESTING_APP_NAME)); + displayNetworkBoostNotification(); + + // verify network boost notification was shown + ArgumentCaptor<Notification> captor = ArgumentCaptor.forClass(Notification.class); + verify(mNotificationManager).notifyAsUser( + eq(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG), + eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY), + captor.capture(), + eq(UserHandle.ALL)); + + // verify notification fields + Notification notification = captor.getValue(); + assertEquals(mContentIntent1, notification.contentIntent); + assertEquals(mPendingIntent, notification.deleteIntent); + assertEquals(2, notification.actions.length); + assertEquals(mCanceledIntent, notification.actions[0].actionIntent); + assertEquals(mContentIntent2, notification.actions[1].actionIntent); + // verify SlicePurchaseController was notified + verify(mNotificationShownIntent).send(); + } + + private void displayNetworkBoostNotification() { // set up pending intents doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(mPendingIntent).getCreatorPackage(); doReturn(true).when(mPendingIntent).isBroadcast(); @@ -161,14 +181,7 @@ public class SlicePurchaseBroadcastReceiverTest { eq(SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN), eq(PendingIntent.class)); - // set up notification - doReturn(mResources).when(mContext).getResources(); - doReturn(mDisplayMetrics).when(mResources).getDisplayMetrics(); - doReturn("").when(mResources).getString(anyInt()); - doReturn(mApplicationInfo).when(mContext).getApplicationInfo(); - doReturn(mPackageManager).when(mContext).getPackageManager(); - - // set up intents created by broadcast receiver + // spy notification intents to prevent PendingIntent issues doReturn(mContentIntent1).when(mSlicePurchaseBroadcastReceiver).createContentIntent( eq(mContext), eq(mIntent), eq(1)); doReturn(mContentIntent2).when(mSlicePurchaseBroadcastReceiver).createContentIntent( @@ -176,35 +189,28 @@ public class SlicePurchaseBroadcastReceiverTest { doReturn(mCanceledIntent).when(mSlicePurchaseBroadcastReceiver).createCanceledIntent( eq(mContext), eq(mIntent)); + // spy resources to prevent resource not found issues + doReturn(mResources).when(mSlicePurchaseBroadcastReceiver).getResources(eq(mContext)); + // send ACTION_START_SLICE_PURCHASE_APP + doReturn(SlicePurchaseController.ACTION_START_SLICE_PURCHASE_APP).when(mIntent).getAction(); + doReturn(PHONE_ID).when(mIntent).getIntExtra( + eq(SlicePurchaseController.EXTRA_PHONE_ID), anyInt()); + doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mIntent).getIntExtra( + eq(SlicePurchaseController.EXTRA_SUB_ID), anyInt()); + doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra( + eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt()); + doReturn(TAG).when(mIntent).getStringExtra( + eq(SlicePurchaseController.EXTRA_REQUESTING_APP_NAME)); mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent); - - // verify network boost notification was shown - verify(mNotificationManager).notifyAsUser( - eq(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG), - eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY), - mNotificationCaptor.capture(), - eq(UserHandle.ALL)); - - Notification notification = mNotificationCaptor.getValue(); - assertEquals(mContentIntent1, notification.contentIntent); - assertEquals(mPendingIntent, notification.deleteIntent); - assertEquals(2, notification.actions.length); - assertEquals(mCanceledIntent, notification.actions[0].actionIntent); - assertEquals(mContentIntent2, notification.actions[1].actionIntent); - - // verify SlicePurchaseController was notified - verify(mNotificationShownIntent).send(); } @Test public void testNotificationCanceled() { - // set up intent + // send ACTION_NOTIFICATION_CANCELED doReturn("com.android.phone.slice.action.NOTIFICATION_CANCELED").when(mIntent).getAction(); doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra( eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt()); - - // send ACTION_NOTIFICATION_CANCELED mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent); // verify notification was canceled @@ -215,14 +221,14 @@ public class SlicePurchaseBroadcastReceiverTest { } @Test - public void testNotificationTimeout() { - // set up intent + public void testNotificationTimeout() throws Exception { + displayNetworkBoostNotification(); + + // send ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT doReturn(SlicePurchaseController.ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT).when(mIntent) .getAction(); doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra( eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt()); - - // send ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent); // verify notification was canceled @@ -233,27 +239,52 @@ public class SlicePurchaseBroadcastReceiverTest { } @Test - // TODO: WebView/Activity should not close on timeout. - // This test should be removed once implementation is fixed. - public void testActivityTimeout() { - // create and track activity - SlicePurchaseBroadcastReceiver.updateSlicePurchaseActivity( - TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mSlicePurchaseActivity); - - // set up intent - doReturn(SlicePurchaseController.ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT).when(mIntent) - .getAction(); - doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra( - eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt()); - - // send ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT + public void testLocaleChanged() throws Exception { + // get previous locale + doReturn(mConfiguration).when(mSpiedResources).getConfiguration(); + Locale before = getLocale(); + + // display notification + displayNetworkBoostNotification(); + clearInvocations(mNotificationManager); + clearInvocations(mNotificationShownIntent); + + // change current locale from previous value + Locale newLocale = Locale.forLanguageTag("en-US"); + if (before.equals(newLocale)) { + newLocale = Locale.forLanguageTag("ko-KR"); + } + doReturn(newLocale).when(mSlicePurchaseBroadcastReceiver).getCurrentLocale(); + + // send ACTION_LOCALE_CHANGED + doReturn(Intent.ACTION_LOCALE_CHANGED).when(mIntent).getAction(); mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent); - // verify activity was canceled - verify(mSlicePurchaseActivity).finishAndRemoveTask(); + // verify notification was updated and SlicePurchaseController was not notified + verify(mNotificationManager).cancelAsUser( + eq(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG), + eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY), + eq(UserHandle.ALL)); + verify(mNotificationManager).notifyAsUser( + eq(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG), + eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY), + any(Notification.class), + eq(UserHandle.ALL)); + verify(mNotificationShownIntent, never()).send(); + + // verify locale was changed successfully + doCallRealMethod().when(mSlicePurchaseBroadcastReceiver).getResources(eq(mContext)); + assertEquals(newLocale, getLocale()); + } - // untrack activity - SlicePurchaseBroadcastReceiver.removeSlicePurchaseActivity( - TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY); + private Locale getLocale() { + try { + mSlicePurchaseBroadcastReceiver.getResources(mContext); + fail("getLocale should not have completed successfully."); + } catch (NullPointerException expected) { } + ArgumentCaptor<Locale> captor = ArgumentCaptor.forClass(Locale.class); + verify(mConfiguration).setLocale(captor.capture()); + clearInvocations(mConfiguration); + return captor.getValue(); } } |